md4ai 0.9.9 → 0.10.0

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.
Files changed (2) hide show
  1. package/dist/index.bundled.js +197 -23
  2. package/package.json +1 -1
@@ -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.10.0" : "0.0.0-dev";
1649
1727
  }
1650
1728
  });
1651
1729
 
@@ -3825,30 +3903,122 @@ async function updateCommand(options) {
3825
3903
  spawnPostUpdate();
3826
3904
  }
3827
3905
 
3906
+ // dist/commands/start.js
3907
+ init_check_update();
3908
+ init_map();
3909
+ init_mcp_watch();
3910
+ import chalk23 from "chalk";
3911
+ import { resolve as resolve6 } from "node:path";
3912
+ async function fetchLatestVersion2() {
3913
+ try {
3914
+ const controller = new AbortController();
3915
+ const timeout = setTimeout(() => controller.abort(), 5e3);
3916
+ const res = await fetch("https://registry.npmjs.org/md4ai/latest", {
3917
+ signal: controller.signal
3918
+ });
3919
+ clearTimeout(timeout);
3920
+ if (!res.ok)
3921
+ return null;
3922
+ const data = await res.json();
3923
+ return data.version ?? null;
3924
+ } catch {
3925
+ return null;
3926
+ }
3927
+ }
3928
+ function isNewer3(a, b) {
3929
+ const pa = a.split(".").map(Number);
3930
+ const pb = b.split(".").map(Number);
3931
+ for (let i = 0; i < 3; i++) {
3932
+ if ((pa[i] ?? 0) > (pb[i] ?? 0))
3933
+ return true;
3934
+ if ((pa[i] ?? 0) < (pb[i] ?? 0))
3935
+ return false;
3936
+ }
3937
+ return false;
3938
+ }
3939
+ async function startCommand() {
3940
+ const projectRoot = resolve6(process.cwd());
3941
+ console.log("");
3942
+ console.log(chalk23.bold.cyan(` MD4AI v${CURRENT_VERSION}`));
3943
+ console.log(chalk23.dim(` ${projectRoot}`));
3944
+ console.log("");
3945
+ console.log(chalk23.blue(" \u2460 Checking for updates..."));
3946
+ const latest = await fetchLatestVersion2();
3947
+ if (latest && isNewer3(latest, CURRENT_VERSION)) {
3948
+ console.log(chalk23.yellow(` Update available: v${CURRENT_VERSION} \u2192 v${latest}`));
3949
+ if (process.stdin.isTTY) {
3950
+ const { confirm: confirm4 } = await import("@inquirer/prompts");
3951
+ const wantUpdate = await confirm4({
3952
+ message: `Install md4ai v${latest} now?`,
3953
+ default: true
3954
+ });
3955
+ if (wantUpdate) {
3956
+ const { execFileSync: execFileSync7 } = await import("node:child_process");
3957
+ console.log(chalk23.blue("\n Installing...\n"));
3958
+ try {
3959
+ execFileSync7("npm", ["install", "-g", "md4ai"], {
3960
+ stdio: "inherit",
3961
+ timeout: 6e4
3962
+ });
3963
+ console.log(chalk23.green(" Updated successfully."));
3964
+ console.log(chalk23.dim(" Restarting with the new version...\n"));
3965
+ const { spawn: spawn2 } = await import("node:child_process");
3966
+ const child = spawn2("md4ai", ["start"], { stdio: "inherit", shell: false });
3967
+ child.on("exit", (code) => process.exit(code ?? 0));
3968
+ return;
3969
+ } catch {
3970
+ console.log(chalk23.yellow(" Update failed \u2014 continuing with current version.\n"));
3971
+ }
3972
+ } else {
3973
+ console.log("");
3974
+ }
3975
+ } else {
3976
+ console.log(chalk23.dim(" Run md4ai update to install it.\n"));
3977
+ }
3978
+ } else if (latest) {
3979
+ console.log(chalk23.green(" You're on the latest version.\n"));
3980
+ } else {
3981
+ console.log(chalk23.dim(" Could not reach npm registry \u2014 skipping.\n"));
3982
+ }
3983
+ console.log(chalk23.blue(" \u2461 Scanning project files..."));
3984
+ console.log("");
3985
+ await mapCommand(projectRoot, {});
3986
+ if (!process.stdin.isTTY) {
3987
+ console.log(chalk23.dim("\n Non-interactive mode \u2014 skipping MCP monitor."));
3988
+ return;
3989
+ }
3990
+ console.log("");
3991
+ console.log(chalk23.blue(" \u2462 Starting MCP monitor..."));
3992
+ console.log("");
3993
+ await mcpWatchCommand();
3994
+ }
3995
+
3828
3996
  // dist/commands/config.js
3829
3997
  init_config();
3830
- import chalk23 from "chalk";
3998
+ import chalk24 from "chalk";
3831
3999
  var ALLOWED_KEYS = ["vercel-token"];
3832
4000
  var KEY_MAP = {
3833
4001
  "vercel-token": "vercelToken"
3834
4002
  };
3835
4003
  async function configSetCommand(key, value) {
3836
4004
  if (!ALLOWED_KEYS.includes(key)) {
3837
- console.error(chalk23.red(`Unknown config key: ${key}`));
4005
+ console.error(chalk24.red(`Unknown config key: ${key}`));
3838
4006
  console.log(` Allowed keys: ${ALLOWED_KEYS.join(", ")}`);
3839
4007
  process.exit(1);
3840
4008
  }
3841
4009
  const credKey = KEY_MAP[key];
3842
4010
  await mergeCredentials({ [credKey]: value });
3843
- console.log(chalk23.green(`Saved ${key}.`));
4011
+ console.log(chalk24.green(`Saved ${key}.`));
3844
4012
  }
3845
4013
 
3846
4014
  // dist/index.js
3847
4015
  init_check_update();
3848
4016
  var program = new Command();
3849
4017
  program.name("md4ai").description("MD4AI \u2014 Claude tooling visualiser").version(CURRENT_VERSION).addHelpText("after", `
3850
- Update to the latest version:
3851
- md4ai update`);
4018
+ Quick start:
4019
+ md4ai start Check for updates, scan project, and start MCP monitoring
4020
+ md4ai Same as md4ai start (when run in a linked project folder)`);
4021
+ program.command("start").description("Check for updates, scan project, and start MCP monitoring \u2014 the all-in-one command").action(startCommand);
3852
4022
  program.command("login").description("Log in to MD4AI with email and password").action(loginCommand);
3853
4023
  program.command("logout").description("Log out and clear stored credentials").action(logoutCommand);
3854
4024
  program.command("status").description("Show login status, device count, folder count, last sync").action(statusCommand);
@@ -3871,9 +4041,13 @@ var admin = program.command("admin").description("Admin commands for managing th
3871
4041
  admin.command("update-tool").description("Add or update a tool in the master registry").requiredOption("--name <name>", "Canonical tool name (e.g. next, playwright)").option("--display <display>", 'Human-friendly display name (e.g. "Next.js")').option("--category <category>", "Tool category (framework|runtime|cli|mcp|package|database|other)").option("--stable <version>", "Latest stable version").option("--beta <version>", "Latest beta/RC version").option("--source <url>", "Source of truth URL for checking versions").option("--install <url>", "Download/install link").option("--notes <text>", "Compatibility notes or warnings").action(adminUpdateToolCommand);
3872
4042
  admin.command("list-tools").description("List all tools in the master registry").action(adminListToolsCommand);
3873
4043
  admin.command("fetch-versions").description("Fetch latest stable and beta versions from npm and GitHub").action(adminFetchVersionsCommand);
3874
- program.parse();
3875
- var ran = program.args[0];
3876
- var skipAutoCheck = ["update", "check-update", "mcp-watch"];
3877
- if (!skipAutoCheck.includes(ran)) {
3878
- autoCheckForUpdate();
4044
+ if (process.argv.length <= 2) {
4045
+ void startCommand();
4046
+ } else {
4047
+ program.parse();
4048
+ const ran = program.args[0];
4049
+ const skipAutoCheck = ["update", "check-update", "mcp-watch", "start"];
4050
+ if (!skipAutoCheck.includes(ran)) {
4051
+ autoCheckForUpdate();
4052
+ }
3879
4053
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md4ai",
3
- "version": "0.9.9",
3
+ "version": "0.10.0",
4
4
  "description": "CLI for MD4AI — scan Claude projects and sync to your dashboard",
5
5
  "type": "module",
6
6
  "bin": {