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.
- package/dist/index.bundled.js +197 -23
- package/package.json +1 -1
package/dist/index.bundled.js
CHANGED
|
@@ -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
|
|
890
|
+
async function resolveVercelTokenWithSource() {
|
|
891
891
|
const creds = await loadCredentials();
|
|
892
|
-
if (creds?.vercelToken)
|
|
893
|
-
return
|
|
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
|
-
|
|
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
|
|
1017
|
-
if (!
|
|
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
|
-
|
|
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.
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
3851
|
-
md4ai
|
|
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
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
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
|
}
|