@leanmcp/cli 0.5.7 → 0.5.8
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 +165 -113
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import fs13 from "fs-extra";
|
|
7
|
+
import path13 from "path";
|
|
8
8
|
import ora9 from "ora";
|
|
9
9
|
import { createRequire } from "module";
|
|
10
10
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
@@ -13,8 +13,8 @@ import { spawn as spawn4 } from "child_process";
|
|
|
13
13
|
// src/commands/dev.ts
|
|
14
14
|
import { spawn } from "child_process";
|
|
15
15
|
import ora from "ora";
|
|
16
|
-
import
|
|
17
|
-
import
|
|
16
|
+
import path4 from "path";
|
|
17
|
+
import fs4 from "fs-extra";
|
|
18
18
|
import crypto2 from "crypto";
|
|
19
19
|
import chokidar from "chokidar";
|
|
20
20
|
|
|
@@ -471,6 +471,8 @@ __name(deleteUIComponent, "deleteUIComponent");
|
|
|
471
471
|
import chalk from "chalk";
|
|
472
472
|
import os from "os";
|
|
473
473
|
import crypto from "crypto";
|
|
474
|
+
import path3 from "path";
|
|
475
|
+
import fs3 from "fs-extra";
|
|
474
476
|
import { execSync } from "child_process";
|
|
475
477
|
var POSTHOG_API_KEY = "phc_EoMHKFbx6j2wUFsf8ywqgHntY4vEXC3ZzLFoPJVjRRT";
|
|
476
478
|
var POSTHOG_API_HOST = "https://d18m0xvdtnkibr.cloudfront.net";
|
|
@@ -568,12 +570,40 @@ var sendToPostHog = /* @__PURE__ */ __name((eventName, properties = {}) => {
|
|
|
568
570
|
}
|
|
569
571
|
});
|
|
570
572
|
}, "sendToPostHog");
|
|
573
|
+
var LOG_DIR = path3.join(os.homedir(), ".leanmcp", "logs");
|
|
574
|
+
try {
|
|
575
|
+
fs3.ensureDirSync(LOG_DIR);
|
|
576
|
+
} catch (err) {
|
|
577
|
+
}
|
|
578
|
+
function getTimestamp() {
|
|
579
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
580
|
+
}
|
|
581
|
+
__name(getTimestamp, "getTimestamp");
|
|
582
|
+
function getLogFilePath() {
|
|
583
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
584
|
+
return path3.join(LOG_DIR, `cli-${date}.log`);
|
|
585
|
+
}
|
|
586
|
+
__name(getLogFilePath, "getLogFilePath");
|
|
587
|
+
function redactSensitive(text) {
|
|
588
|
+
if (!text) return text;
|
|
589
|
+
return text.replace(/(leanmcp_|airtain_)[a-zA-Z0-9]{20,}/g, "$1********************");
|
|
590
|
+
}
|
|
591
|
+
__name(redactSensitive, "redactSensitive");
|
|
571
592
|
var LoggerClass = class LoggerClass2 {
|
|
572
593
|
static {
|
|
573
594
|
__name(this, "LoggerClass");
|
|
574
595
|
}
|
|
596
|
+
writeToFile(level, message) {
|
|
597
|
+
try {
|
|
598
|
+
const timestamp = getTimestamp();
|
|
599
|
+
const logLine = `[${timestamp}] [${level.toUpperCase()}] ${redactSensitive(message)}
|
|
600
|
+
`;
|
|
601
|
+
fs3.appendFileSync(getLogFilePath(), logLine);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
}
|
|
604
|
+
}
|
|
575
605
|
/**
|
|
576
|
-
* Log a message to console and send cli_log event to PostHog
|
|
606
|
+
* Log a message to console, file, and send cli_log event to PostHog
|
|
577
607
|
* @param text - The text to log
|
|
578
608
|
* @param styleFn - Optional chalk style function (e.g., chalk.cyan, chalk.green)
|
|
579
609
|
*/
|
|
@@ -583,32 +613,35 @@ var LoggerClass = class LoggerClass2 {
|
|
|
583
613
|
} else {
|
|
584
614
|
console.log(text);
|
|
585
615
|
}
|
|
616
|
+
this.writeToFile("log", text);
|
|
586
617
|
sendToPostHog("cli_log", {
|
|
587
618
|
message: text,
|
|
588
619
|
level: "log"
|
|
589
620
|
});
|
|
590
621
|
}
|
|
591
622
|
/**
|
|
592
|
-
* Log a warning to console and send cli_warn event to PostHog
|
|
623
|
+
* Log a warning to console, file, and send cli_warn event to PostHog
|
|
593
624
|
* @param text - The warning text
|
|
594
625
|
* @param styleFn - Optional chalk style function (defaults to chalk.yellow)
|
|
595
626
|
*/
|
|
596
627
|
warn(text, styleFn) {
|
|
597
628
|
const style = styleFn || chalk.yellow;
|
|
598
629
|
console.log(style(text));
|
|
630
|
+
this.writeToFile("warn", text);
|
|
599
631
|
sendToPostHog("cli_warn", {
|
|
600
632
|
message: text,
|
|
601
633
|
level: "warn"
|
|
602
634
|
});
|
|
603
635
|
}
|
|
604
636
|
/**
|
|
605
|
-
* Log an error to console and send cli_error event to PostHog
|
|
637
|
+
* Log an error to console, file, and send cli_error event to PostHog
|
|
606
638
|
* @param text - The error text
|
|
607
639
|
* @param styleFn - Optional chalk style function (defaults to chalk.red)
|
|
608
640
|
*/
|
|
609
641
|
error(text, styleFn) {
|
|
610
642
|
const style = styleFn || chalk.red;
|
|
611
643
|
console.error(style(text));
|
|
644
|
+
this.writeToFile("error", text);
|
|
612
645
|
sendToPostHog("cli_error", {
|
|
613
646
|
message: text,
|
|
614
647
|
level: "error"
|
|
@@ -620,6 +653,7 @@ var LoggerClass = class LoggerClass2 {
|
|
|
620
653
|
*/
|
|
621
654
|
info(text) {
|
|
622
655
|
console.log(chalk.cyan(text));
|
|
656
|
+
this.writeToFile("info", text);
|
|
623
657
|
sendToPostHog("cli_log", {
|
|
624
658
|
message: text,
|
|
625
659
|
level: "info"
|
|
@@ -631,6 +665,7 @@ var LoggerClass = class LoggerClass2 {
|
|
|
631
665
|
*/
|
|
632
666
|
success(text) {
|
|
633
667
|
console.log(chalk.green(text));
|
|
668
|
+
this.writeToFile("success", text);
|
|
634
669
|
sendToPostHog("cli_log", {
|
|
635
670
|
message: text,
|
|
636
671
|
level: "success"
|
|
@@ -642,6 +677,7 @@ var LoggerClass = class LoggerClass2 {
|
|
|
642
677
|
*/
|
|
643
678
|
gray(text) {
|
|
644
679
|
console.log(chalk.gray(text));
|
|
680
|
+
this.writeToFile("debug", text);
|
|
645
681
|
sendToPostHog("cli_log", {
|
|
646
682
|
message: text,
|
|
647
683
|
level: "gray"
|
|
@@ -658,7 +694,7 @@ var trackCommand = /* @__PURE__ */ __name((command, options = {}) => {
|
|
|
658
694
|
|
|
659
695
|
// src/commands/dev.ts
|
|
660
696
|
function computeHash(filePath) {
|
|
661
|
-
const content =
|
|
697
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
662
698
|
return crypto2.createHash("md5").update(content).digest("hex");
|
|
663
699
|
}
|
|
664
700
|
__name(computeHash, "computeHash");
|
|
@@ -675,7 +711,7 @@ function computeDiff(previousUIApps, currentUIApps, hashCache) {
|
|
|
675
711
|
} else {
|
|
676
712
|
const oldHash = hashCache.get(app.resourceUri);
|
|
677
713
|
let newHash;
|
|
678
|
-
if (
|
|
714
|
+
if (fs4.existsSync(app.componentPath)) {
|
|
679
715
|
newHash = computeHash(app.componentPath);
|
|
680
716
|
}
|
|
681
717
|
if (oldHash !== newHash) {
|
|
@@ -692,7 +728,7 @@ function computeDiff(previousUIApps, currentUIApps, hashCache) {
|
|
|
692
728
|
__name(computeDiff, "computeDiff");
|
|
693
729
|
async function devCommand() {
|
|
694
730
|
const cwd = process.cwd();
|
|
695
|
-
if (!await
|
|
731
|
+
if (!await fs4.pathExists(path4.join(cwd, "main.ts"))) {
|
|
696
732
|
logger.error("ERROR: Not a LeanMCP project (main.ts not found).");
|
|
697
733
|
logger.gray("Run this command from your project root.");
|
|
698
734
|
process.exit(1);
|
|
@@ -748,12 +784,12 @@ async function devCommand() {
|
|
|
748
784
|
let watcher = null;
|
|
749
785
|
const componentHashCache = /* @__PURE__ */ new Map();
|
|
750
786
|
for (const app of uiApps) {
|
|
751
|
-
if (await
|
|
787
|
+
if (await fs4.pathExists(app.componentPath)) {
|
|
752
788
|
componentHashCache.set(app.resourceUri, computeHash(app.componentPath));
|
|
753
789
|
}
|
|
754
790
|
}
|
|
755
791
|
let previousUIApps = uiApps;
|
|
756
|
-
const mcpPath =
|
|
792
|
+
const mcpPath = path4.join(cwd, "mcp");
|
|
757
793
|
watcher = chokidar.watch(mcpPath, {
|
|
758
794
|
ignoreInitial: true,
|
|
759
795
|
ignored: [
|
|
@@ -795,7 +831,7 @@ async function devCommand() {
|
|
|
795
831
|
} else {
|
|
796
832
|
manifest[app.resourceUri] = result.htmlPath;
|
|
797
833
|
}
|
|
798
|
-
if (await
|
|
834
|
+
if (await fs4.pathExists(app.componentPath)) {
|
|
799
835
|
componentHashCache.set(app.resourceUri, computeHash(app.componentPath));
|
|
800
836
|
}
|
|
801
837
|
logger.success(`${app.componentName} ${action.toLowerCase()} complete`);
|
|
@@ -841,12 +877,12 @@ __name(devCommand, "devCommand");
|
|
|
841
877
|
// src/commands/build.ts
|
|
842
878
|
import { spawn as spawn2 } from "child_process";
|
|
843
879
|
import ora2 from "ora";
|
|
844
|
-
import
|
|
845
|
-
import
|
|
880
|
+
import path6 from "path";
|
|
881
|
+
import fs6 from "fs-extra";
|
|
846
882
|
|
|
847
883
|
// src/schema-extractor.ts
|
|
848
|
-
import
|
|
849
|
-
import
|
|
884
|
+
import path5 from "path";
|
|
885
|
+
import fs5 from "fs-extra";
|
|
850
886
|
import { Project, Node } from "ts-morph";
|
|
851
887
|
async function generateSchemaMetadata(projectDir) {
|
|
852
888
|
const metadata = {};
|
|
@@ -921,9 +957,9 @@ async function generateSchemaMetadata(projectDir) {
|
|
|
921
957
|
}
|
|
922
958
|
processedClasses.add(className);
|
|
923
959
|
}
|
|
924
|
-
const outputPath =
|
|
925
|
-
await
|
|
926
|
-
await
|
|
960
|
+
const outputPath = path5.join(projectDir, "dist", "schema-metadata.json");
|
|
961
|
+
await fs5.ensureDir(path5.dirname(outputPath));
|
|
962
|
+
await fs5.writeJSON(outputPath, metadata, {
|
|
927
963
|
spaces: 2
|
|
928
964
|
});
|
|
929
965
|
}
|
|
@@ -958,11 +994,11 @@ __name(extractBaseClassName, "extractBaseClassName");
|
|
|
958
994
|
async function findTsFiles(dir) {
|
|
959
995
|
const files = [];
|
|
960
996
|
async function scan(currentDir) {
|
|
961
|
-
const entries = await
|
|
997
|
+
const entries = await fs5.readdir(currentDir, {
|
|
962
998
|
withFileTypes: true
|
|
963
999
|
});
|
|
964
1000
|
for (const entry of entries) {
|
|
965
|
-
const fullPath =
|
|
1001
|
+
const fullPath = path5.join(currentDir, entry.name);
|
|
966
1002
|
if (entry.isDirectory()) {
|
|
967
1003
|
if ([
|
|
968
1004
|
"node_modules",
|
|
@@ -1028,7 +1064,7 @@ __name(mapPrimitiveType, "mapPrimitiveType");
|
|
|
1028
1064
|
// src/commands/build.ts
|
|
1029
1065
|
async function buildCommand() {
|
|
1030
1066
|
const cwd = process.cwd();
|
|
1031
|
-
if (!await
|
|
1067
|
+
if (!await fs6.pathExists(path6.join(cwd, "main.ts"))) {
|
|
1032
1068
|
logger.error("ERROR: Not a LeanMCP project (main.ts not found).");
|
|
1033
1069
|
logger.gray("Run this command from your project root.");
|
|
1034
1070
|
process.exit(1);
|
|
@@ -1110,11 +1146,11 @@ __name(buildCommand, "buildCommand");
|
|
|
1110
1146
|
// src/commands/start.ts
|
|
1111
1147
|
import { spawn as spawn3 } from "child_process";
|
|
1112
1148
|
import ora3 from "ora";
|
|
1113
|
-
import
|
|
1114
|
-
import
|
|
1149
|
+
import path7 from "path";
|
|
1150
|
+
import fs7 from "fs-extra";
|
|
1115
1151
|
async function startCommand() {
|
|
1116
1152
|
const cwd = process.cwd();
|
|
1117
|
-
if (!await
|
|
1153
|
+
if (!await fs7.pathExists(path7.join(cwd, "main.ts"))) {
|
|
1118
1154
|
logger.error("ERROR: Not a LeanMCP project (main.ts not found).");
|
|
1119
1155
|
logger.gray("Run this command from your project root.");
|
|
1120
1156
|
process.exit(1);
|
|
@@ -1216,8 +1252,8 @@ __name(startCommand, "startCommand");
|
|
|
1216
1252
|
|
|
1217
1253
|
// src/commands/login.ts
|
|
1218
1254
|
import ora4 from "ora";
|
|
1219
|
-
import
|
|
1220
|
-
import
|
|
1255
|
+
import path8 from "path";
|
|
1256
|
+
import fs8 from "fs-extra";
|
|
1221
1257
|
import os2 from "os";
|
|
1222
1258
|
import { input, confirm } from "@inquirer/prompts";
|
|
1223
1259
|
var DEBUG_MODE2 = false;
|
|
@@ -1231,12 +1267,12 @@ function debug2(message, ...args) {
|
|
|
1231
1267
|
}
|
|
1232
1268
|
}
|
|
1233
1269
|
__name(debug2, "debug");
|
|
1234
|
-
var CONFIG_DIR =
|
|
1235
|
-
var CONFIG_FILE =
|
|
1270
|
+
var CONFIG_DIR = path8.join(os2.homedir(), ".leanmcp");
|
|
1271
|
+
var CONFIG_FILE = path8.join(CONFIG_DIR, "config.json");
|
|
1236
1272
|
async function loadConfig() {
|
|
1237
1273
|
try {
|
|
1238
|
-
if (await
|
|
1239
|
-
return await
|
|
1274
|
+
if (await fs8.pathExists(CONFIG_FILE)) {
|
|
1275
|
+
return await fs8.readJSON(CONFIG_FILE);
|
|
1240
1276
|
}
|
|
1241
1277
|
} catch (error) {
|
|
1242
1278
|
}
|
|
@@ -1244,8 +1280,8 @@ async function loadConfig() {
|
|
|
1244
1280
|
}
|
|
1245
1281
|
__name(loadConfig, "loadConfig");
|
|
1246
1282
|
async function saveConfig(config) {
|
|
1247
|
-
await
|
|
1248
|
-
await
|
|
1283
|
+
await fs8.ensureDir(CONFIG_DIR);
|
|
1284
|
+
await fs8.writeJSON(CONFIG_FILE, config, {
|
|
1249
1285
|
spaces: 2
|
|
1250
1286
|
});
|
|
1251
1287
|
}
|
|
@@ -1452,8 +1488,8 @@ __name(whoamiCommand, "whoamiCommand");
|
|
|
1452
1488
|
|
|
1453
1489
|
// src/commands/deploy.ts
|
|
1454
1490
|
import ora5 from "ora";
|
|
1455
|
-
import
|
|
1456
|
-
import
|
|
1491
|
+
import path10 from "path";
|
|
1492
|
+
import fs10 from "fs-extra";
|
|
1457
1493
|
import os3 from "os";
|
|
1458
1494
|
import archiver from "archiver";
|
|
1459
1495
|
import { input as input2, confirm as confirm2, select } from "@inquirer/prompts";
|
|
@@ -1636,8 +1672,8 @@ function generateProjectName() {
|
|
|
1636
1672
|
__name(generateProjectName, "generateProjectName");
|
|
1637
1673
|
|
|
1638
1674
|
// src/utils/env-parser.ts
|
|
1639
|
-
import
|
|
1640
|
-
import
|
|
1675
|
+
import fs9 from "fs-extra";
|
|
1676
|
+
import path9 from "path";
|
|
1641
1677
|
var RESERVED_ENV_KEYS = [
|
|
1642
1678
|
"AWS_REGION",
|
|
1643
1679
|
"AWS_ACCESS_KEY_ID",
|
|
@@ -1700,16 +1736,16 @@ function parseEnvFile(content) {
|
|
|
1700
1736
|
}
|
|
1701
1737
|
__name(parseEnvFile, "parseEnvFile");
|
|
1702
1738
|
async function loadEnvFile(filePath) {
|
|
1703
|
-
const absolutePath =
|
|
1704
|
-
if (!await
|
|
1739
|
+
const absolutePath = path9.resolve(filePath);
|
|
1740
|
+
if (!await fs9.pathExists(absolutePath)) {
|
|
1705
1741
|
throw new Error(`Env file not found: ${absolutePath}`);
|
|
1706
1742
|
}
|
|
1707
|
-
const content = await
|
|
1743
|
+
const content = await fs9.readFile(absolutePath, "utf-8");
|
|
1708
1744
|
return parseEnvFile(content);
|
|
1709
1745
|
}
|
|
1710
1746
|
__name(loadEnvFile, "loadEnvFile");
|
|
1711
1747
|
async function writeEnvFile(filePath, vars) {
|
|
1712
|
-
const absolutePath =
|
|
1748
|
+
const absolutePath = path9.resolve(filePath);
|
|
1713
1749
|
const lines = [
|
|
1714
1750
|
"# Environment variables",
|
|
1715
1751
|
`# Generated by leanmcp CLI at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -1725,7 +1761,7 @@ async function writeEnvFile(filePath, vars) {
|
|
|
1725
1761
|
}
|
|
1726
1762
|
}
|
|
1727
1763
|
lines.push("");
|
|
1728
|
-
await
|
|
1764
|
+
await fs9.writeFile(absolutePath, lines.join("\n"));
|
|
1729
1765
|
}
|
|
1730
1766
|
__name(writeEnvFile, "writeEnvFile");
|
|
1731
1767
|
function isValidEnvKey(key) {
|
|
@@ -1839,10 +1875,10 @@ var API_ENDPOINTS = {
|
|
|
1839
1875
|
var LEANMCP_CONFIG_DIR = ".leanmcp";
|
|
1840
1876
|
var LEANMCP_CONFIG_FILE = "config.json";
|
|
1841
1877
|
async function readLeanMCPConfig(projectPath) {
|
|
1842
|
-
const configPath =
|
|
1878
|
+
const configPath = path10.join(projectPath, LEANMCP_CONFIG_DIR, LEANMCP_CONFIG_FILE);
|
|
1843
1879
|
try {
|
|
1844
|
-
if (await
|
|
1845
|
-
const config = await
|
|
1880
|
+
if (await fs10.pathExists(configPath)) {
|
|
1881
|
+
const config = await fs10.readJSON(configPath);
|
|
1846
1882
|
debug3("Found existing .leanmcp config:", config);
|
|
1847
1883
|
return config;
|
|
1848
1884
|
}
|
|
@@ -1853,10 +1889,10 @@ async function readLeanMCPConfig(projectPath) {
|
|
|
1853
1889
|
}
|
|
1854
1890
|
__name(readLeanMCPConfig, "readLeanMCPConfig");
|
|
1855
1891
|
async function writeLeanMCPConfig(projectPath, config) {
|
|
1856
|
-
const configDir =
|
|
1857
|
-
const configPath =
|
|
1858
|
-
await
|
|
1859
|
-
await
|
|
1892
|
+
const configDir = path10.join(projectPath, LEANMCP_CONFIG_DIR);
|
|
1893
|
+
const configPath = path10.join(configDir, LEANMCP_CONFIG_FILE);
|
|
1894
|
+
await fs10.ensureDir(configDir);
|
|
1895
|
+
await fs10.writeJSON(configPath, config, {
|
|
1860
1896
|
spaces: 2
|
|
1861
1897
|
});
|
|
1862
1898
|
debug3("Saved .leanmcp config:", config);
|
|
@@ -1864,7 +1900,7 @@ async function writeLeanMCPConfig(projectPath, config) {
|
|
|
1864
1900
|
__name(writeLeanMCPConfig, "writeLeanMCPConfig");
|
|
1865
1901
|
async function createZipArchive(folderPath, outputPath) {
|
|
1866
1902
|
return new Promise((resolve, reject) => {
|
|
1867
|
-
const output =
|
|
1903
|
+
const output = fs10.createWriteStream(outputPath);
|
|
1868
1904
|
const archive = archiver("zip", {
|
|
1869
1905
|
zlib: {
|
|
1870
1906
|
level: 9
|
|
@@ -1982,16 +2018,16 @@ async function deployCommand(folderPath, options = {}) {
|
|
|
1982
2018
|
}
|
|
1983
2019
|
const apiUrl = await getApiUrl();
|
|
1984
2020
|
debug3("API URL:", apiUrl);
|
|
1985
|
-
const absolutePath =
|
|
1986
|
-
if (!await
|
|
2021
|
+
const absolutePath = path10.resolve(process.cwd(), folderPath);
|
|
2022
|
+
if (!await fs10.pathExists(absolutePath)) {
|
|
1987
2023
|
logger.error(`Folder not found: ${absolutePath}`);
|
|
1988
2024
|
process.exit(1);
|
|
1989
2025
|
}
|
|
1990
|
-
const hasMainTs = await
|
|
1991
|
-
const hasPackageJson = await
|
|
1992
|
-
const hasMainPy = await
|
|
1993
|
-
const hasRequirementsTxt = await
|
|
1994
|
-
const hasPyprojectToml = await
|
|
2026
|
+
const hasMainTs = await fs10.pathExists(path10.join(absolutePath, "main.ts"));
|
|
2027
|
+
const hasPackageJson = await fs10.pathExists(path10.join(absolutePath, "package.json"));
|
|
2028
|
+
const hasMainPy = await fs10.pathExists(path10.join(absolutePath, "main.py"));
|
|
2029
|
+
const hasRequirementsTxt = await fs10.pathExists(path10.join(absolutePath, "requirements.txt"));
|
|
2030
|
+
const hasPyprojectToml = await fs10.pathExists(path10.join(absolutePath, "pyproject.toml"));
|
|
1995
2031
|
const isNodeProject = hasMainTs || hasPackageJson;
|
|
1996
2032
|
const isPythonProject = hasMainPy || hasRequirementsTxt || hasPyprojectToml;
|
|
1997
2033
|
if (!isNodeProject && !isPythonProject) {
|
|
@@ -2063,10 +2099,10 @@ Generated project name: ${chalk.bold(projectName)}
|
|
|
2063
2099
|
} catch (e) {
|
|
2064
2100
|
debug3("Could not fetch existing projects");
|
|
2065
2101
|
}
|
|
2066
|
-
let folderName =
|
|
2102
|
+
let folderName = path10.basename(absolutePath);
|
|
2067
2103
|
if (hasPackageJson) {
|
|
2068
2104
|
try {
|
|
2069
|
-
const pkg2 = await
|
|
2105
|
+
const pkg2 = await fs10.readJSON(path10.join(absolutePath, "package.json"));
|
|
2070
2106
|
folderName = pkg2.name || folderName;
|
|
2071
2107
|
} catch (e) {
|
|
2072
2108
|
}
|
|
@@ -2217,7 +2253,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
2217
2253
|
}
|
|
2218
2254
|
const uploadSpinner = ora5("Packaging and uploading...").start();
|
|
2219
2255
|
try {
|
|
2220
|
-
const tempZip =
|
|
2256
|
+
const tempZip = path10.join(os3.tmpdir(), `leanmcp-${Date.now()}.zip`);
|
|
2221
2257
|
const zipSize = await createZipArchive(absolutePath, tempZip);
|
|
2222
2258
|
uploadSpinner.text = `Packaging... (${Math.round(zipSize / 1024)}KB)`;
|
|
2223
2259
|
debug3("Step 2a: Getting upload URL for project:", projectId);
|
|
@@ -2244,7 +2280,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
2244
2280
|
throw new Error("Backend did not return upload URL");
|
|
2245
2281
|
}
|
|
2246
2282
|
debug3("Step 2b: Uploading to S3...");
|
|
2247
|
-
const zipBuffer = await
|
|
2283
|
+
const zipBuffer = await fs10.readFile(tempZip);
|
|
2248
2284
|
const s3Response = await fetch(uploadUrl, {
|
|
2249
2285
|
method: "PUT",
|
|
2250
2286
|
body: zipBuffer,
|
|
@@ -2267,7 +2303,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
2267
2303
|
s3Location
|
|
2268
2304
|
})
|
|
2269
2305
|
});
|
|
2270
|
-
await
|
|
2306
|
+
await fs10.remove(tempZip);
|
|
2271
2307
|
uploadSpinner.succeed("Project uploaded");
|
|
2272
2308
|
} catch (error) {
|
|
2273
2309
|
uploadSpinner.fail("Failed to upload");
|
|
@@ -2406,8 +2442,8 @@ __name(deployCommand, "deployCommand");
|
|
|
2406
2442
|
|
|
2407
2443
|
// src/commands/feedback.ts
|
|
2408
2444
|
import ora6 from "ora";
|
|
2409
|
-
import
|
|
2410
|
-
import
|
|
2445
|
+
import fs11 from "fs-extra";
|
|
2446
|
+
import path11 from "path";
|
|
2411
2447
|
import os4 from "os";
|
|
2412
2448
|
var DEBUG_MODE4 = false;
|
|
2413
2449
|
function debug4(message, ...args) {
|
|
@@ -2418,13 +2454,13 @@ function debug4(message, ...args) {
|
|
|
2418
2454
|
__name(debug4, "debug");
|
|
2419
2455
|
async function readFileAsBase64(filePath) {
|
|
2420
2456
|
try {
|
|
2421
|
-
const absolutePath =
|
|
2422
|
-
const stats = await
|
|
2457
|
+
const absolutePath = path11.resolve(filePath);
|
|
2458
|
+
const stats = await fs11.stat(absolutePath);
|
|
2423
2459
|
if (!stats.isFile()) {
|
|
2424
2460
|
throw new Error(`${filePath} is not a file`);
|
|
2425
2461
|
}
|
|
2426
|
-
const content = await
|
|
2427
|
-
const ext =
|
|
2462
|
+
const content = await fs11.readFile(absolutePath, "base64");
|
|
2463
|
+
const ext = path11.extname(absolutePath).toLowerCase();
|
|
2428
2464
|
let mimeType = "application/octet-stream";
|
|
2429
2465
|
switch (ext) {
|
|
2430
2466
|
case ".txt":
|
|
@@ -2457,24 +2493,40 @@ __name(readFileAsBase64, "readFileAsBase64");
|
|
|
2457
2493
|
async function collectLogFiles() {
|
|
2458
2494
|
const attachments = [];
|
|
2459
2495
|
const logLocations = [
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2496
|
+
path11.join(os4.homedir(), ".leanmcp", "logs"),
|
|
2497
|
+
path11.join(process.cwd(), "logs"),
|
|
2498
|
+
path11.join(process.cwd(), ".leanmcp", "logs")
|
|
2463
2499
|
];
|
|
2464
2500
|
for (const logDir of logLocations) {
|
|
2465
2501
|
try {
|
|
2466
|
-
if (await
|
|
2467
|
-
const files = await
|
|
2502
|
+
if (await fs11.pathExists(logDir)) {
|
|
2503
|
+
const files = await fs11.readdir(logDir);
|
|
2504
|
+
const logFiles = [];
|
|
2468
2505
|
for (const file of files) {
|
|
2469
|
-
|
|
2506
|
+
if (!file.endsWith(".log")) continue;
|
|
2470
2507
|
try {
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2508
|
+
const filePath = path11.join(logDir, file);
|
|
2509
|
+
const stats = await fs11.stat(filePath);
|
|
2510
|
+
logFiles.push({
|
|
2511
|
+
file,
|
|
2512
|
+
filePath,
|
|
2513
|
+
mtime: stats.mtimeMs
|
|
2475
2514
|
});
|
|
2515
|
+
} catch (e) {
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
const recentLogs = logFiles.sort((a, b) => b.mtime - a.mtime).slice(0, 3);
|
|
2519
|
+
for (const log of recentLogs) {
|
|
2520
|
+
try {
|
|
2521
|
+
const fileData = await readFileAsBase64(log.filePath);
|
|
2522
|
+
if (!attachments.some((a) => a.name === log.file)) {
|
|
2523
|
+
attachments.push({
|
|
2524
|
+
name: log.file,
|
|
2525
|
+
...fileData
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2476
2528
|
} catch (error) {
|
|
2477
|
-
debug4(`Failed to read log file ${filePath}: ${error}`);
|
|
2529
|
+
debug4(`Failed to read log file ${log.filePath}: ${error}`);
|
|
2478
2530
|
}
|
|
2479
2531
|
}
|
|
2480
2532
|
}
|
|
@@ -2482,13 +2534,13 @@ async function collectLogFiles() {
|
|
|
2482
2534
|
debug4(`Failed to scan log directory ${logDir}: ${error}`);
|
|
2483
2535
|
}
|
|
2484
2536
|
}
|
|
2485
|
-
const npmDebugLog =
|
|
2537
|
+
const npmDebugLog = path11.join(os4.homedir(), ".npm", "_logs");
|
|
2486
2538
|
try {
|
|
2487
|
-
if (await
|
|
2488
|
-
const logFiles = await
|
|
2539
|
+
if (await fs11.pathExists(npmDebugLog)) {
|
|
2540
|
+
const logFiles = await fs11.readdir(npmDebugLog);
|
|
2489
2541
|
const latestLog = logFiles.filter((file) => file.endsWith(".log")).sort().pop();
|
|
2490
2542
|
if (latestLog) {
|
|
2491
|
-
const filePath =
|
|
2543
|
+
const filePath = path11.join(npmDebugLog, latestLog);
|
|
2492
2544
|
try {
|
|
2493
2545
|
const fileData = await readFileAsBase64(filePath);
|
|
2494
2546
|
attachments.push({
|
|
@@ -2791,8 +2843,8 @@ __name(projectsDeleteCommand, "projectsDeleteCommand");
|
|
|
2791
2843
|
|
|
2792
2844
|
// src/commands/env.ts
|
|
2793
2845
|
import ora8 from "ora";
|
|
2794
|
-
import
|
|
2795
|
-
import
|
|
2846
|
+
import path12 from "path";
|
|
2847
|
+
import fs12 from "fs-extra";
|
|
2796
2848
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
2797
2849
|
var DEBUG_MODE5 = false;
|
|
2798
2850
|
function setEnvDebugMode(enabled) {
|
|
@@ -2825,10 +2877,10 @@ __name(debugFetch2, "debugFetch");
|
|
|
2825
2877
|
var LEANMCP_CONFIG_DIR2 = ".leanmcp";
|
|
2826
2878
|
var LEANMCP_CONFIG_FILE2 = "config.json";
|
|
2827
2879
|
async function readLeanMCPConfig2(projectPath) {
|
|
2828
|
-
const configPath =
|
|
2880
|
+
const configPath = path12.join(projectPath, LEANMCP_CONFIG_DIR2, LEANMCP_CONFIG_FILE2);
|
|
2829
2881
|
try {
|
|
2830
|
-
if (await
|
|
2831
|
-
const config = await
|
|
2882
|
+
if (await fs12.pathExists(configPath)) {
|
|
2883
|
+
const config = await fs12.readJSON(configPath);
|
|
2832
2884
|
debug5("Found existing .leanmcp config:", config);
|
|
2833
2885
|
return config;
|
|
2834
2886
|
}
|
|
@@ -2846,7 +2898,7 @@ async function getDeploymentContext(folderPath) {
|
|
|
2846
2898
|
return null;
|
|
2847
2899
|
}
|
|
2848
2900
|
const apiUrl = await getApiUrl();
|
|
2849
|
-
const absolutePath =
|
|
2901
|
+
const absolutePath = path12.resolve(process.cwd(), folderPath);
|
|
2850
2902
|
const config = await readLeanMCPConfig2(absolutePath);
|
|
2851
2903
|
if (!config) {
|
|
2852
2904
|
logger.error("No deployment found.");
|
|
@@ -3983,29 +4035,29 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
3983
4035
|
...options
|
|
3984
4036
|
});
|
|
3985
4037
|
const spinner = ora9(`Creating project ${projectName}...`).start();
|
|
3986
|
-
const targetDir =
|
|
3987
|
-
if (
|
|
4038
|
+
const targetDir = path13.join(process.cwd(), projectName);
|
|
4039
|
+
if (fs13.existsSync(targetDir)) {
|
|
3988
4040
|
spinner.fail(`Folder ${projectName} already exists.`);
|
|
3989
4041
|
process.exit(1);
|
|
3990
4042
|
}
|
|
3991
|
-
await
|
|
4043
|
+
await fs13.mkdirp(targetDir);
|
|
3992
4044
|
const isPython = options.python === true;
|
|
3993
4045
|
if (isPython) {
|
|
3994
4046
|
const requirements = getPythonRequirementsTemplate();
|
|
3995
|
-
await
|
|
4047
|
+
await fs13.writeFile(path13.join(targetDir, "requirements.txt"), requirements);
|
|
3996
4048
|
const mainPy = getPythonMainTemplate(projectName);
|
|
3997
|
-
await
|
|
3998
|
-
await
|
|
4049
|
+
await fs13.writeFile(path13.join(targetDir, "main.py"), mainPy);
|
|
4050
|
+
await fs13.writeFile(path13.join(targetDir, ".gitignore"), pythonGitignoreTemplate);
|
|
3999
4051
|
const env = `# Server Configuration
|
|
4000
4052
|
PORT=3001
|
|
4001
4053
|
|
|
4002
4054
|
# Add your environment variables here
|
|
4003
4055
|
`;
|
|
4004
|
-
await
|
|
4056
|
+
await fs13.writeFile(path13.join(targetDir, ".env"), env);
|
|
4005
4057
|
const readme = getPythonReadmeTemplate(projectName);
|
|
4006
|
-
await
|
|
4058
|
+
await fs13.writeFile(path13.join(targetDir, "README.md"), readme);
|
|
4007
4059
|
} else {
|
|
4008
|
-
await
|
|
4060
|
+
await fs13.mkdirp(path13.join(targetDir, "mcp", "example"));
|
|
4009
4061
|
const pkg2 = {
|
|
4010
4062
|
name: projectName,
|
|
4011
4063
|
version: "1.0.0",
|
|
@@ -4041,7 +4093,7 @@ PORT=3001
|
|
|
4041
4093
|
typescript: "^5.6.3"
|
|
4042
4094
|
}
|
|
4043
4095
|
};
|
|
4044
|
-
await
|
|
4096
|
+
await fs13.writeJSON(path13.join(targetDir, "package.json"), pkg2, {
|
|
4045
4097
|
spaces: 2
|
|
4046
4098
|
});
|
|
4047
4099
|
const tsconfig = {
|
|
@@ -4064,15 +4116,15 @@ PORT=3001
|
|
|
4064
4116
|
"dist"
|
|
4065
4117
|
]
|
|
4066
4118
|
};
|
|
4067
|
-
await
|
|
4119
|
+
await fs13.writeJSON(path13.join(targetDir, "tsconfig.json"), tsconfig, {
|
|
4068
4120
|
spaces: 2
|
|
4069
4121
|
});
|
|
4070
4122
|
const dashboardLine = options.dashboard === false ? `
|
|
4071
4123
|
dashboard: false, // Dashboard disabled via --no-dashboard` : "";
|
|
4072
4124
|
const mainTs = getMainTsTemplate(projectName, dashboardLine);
|
|
4073
|
-
await
|
|
4125
|
+
await fs13.writeFile(path13.join(targetDir, "main.ts"), mainTs);
|
|
4074
4126
|
const exampleServiceTs = getExampleServiceTemplate(projectName);
|
|
4075
|
-
await
|
|
4127
|
+
await fs13.writeFile(path13.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
|
|
4076
4128
|
const gitignore = gitignoreTemplate;
|
|
4077
4129
|
const env = `# Server Configuration
|
|
4078
4130
|
PORT=3001
|
|
@@ -4080,10 +4132,10 @@ NODE_ENV=development
|
|
|
4080
4132
|
|
|
4081
4133
|
# Add your environment variables here
|
|
4082
4134
|
`;
|
|
4083
|
-
await
|
|
4084
|
-
await
|
|
4135
|
+
await fs13.writeFile(path13.join(targetDir, ".gitignore"), gitignore);
|
|
4136
|
+
await fs13.writeFile(path13.join(targetDir, ".env"), env);
|
|
4085
4137
|
const readme = getReadmeTemplate(projectName);
|
|
4086
|
-
await
|
|
4138
|
+
await fs13.writeFile(path13.join(targetDir, "README.md"), readme);
|
|
4087
4139
|
}
|
|
4088
4140
|
spinner.succeed(`Project ${projectName} created!`);
|
|
4089
4141
|
logger.log("\nSuccess! Your MCP server is ready.\n", chalk.green);
|
|
@@ -4216,20 +4268,20 @@ program.command("send-feedback [message]").description("Send feedback to the Lea
|
|
|
4216
4268
|
});
|
|
4217
4269
|
program.command("add <serviceName>").description("Add a new MCP service to your project").action(async (serviceName) => {
|
|
4218
4270
|
const cwd = process.cwd();
|
|
4219
|
-
const mcpDir =
|
|
4220
|
-
if (!
|
|
4271
|
+
const mcpDir = path13.join(cwd, "mcp");
|
|
4272
|
+
if (!fs13.existsSync(path13.join(cwd, "main.ts"))) {
|
|
4221
4273
|
logger.log("ERROR: Not a LeanMCP project (main.ts missing).", chalk.red);
|
|
4222
4274
|
process.exit(1);
|
|
4223
4275
|
}
|
|
4224
|
-
const serviceDir =
|
|
4225
|
-
const serviceFile =
|
|
4226
|
-
if (
|
|
4276
|
+
const serviceDir = path13.join(mcpDir, serviceName);
|
|
4277
|
+
const serviceFile = path13.join(serviceDir, "index.ts");
|
|
4278
|
+
if (fs13.existsSync(serviceDir)) {
|
|
4227
4279
|
logger.log(`ERROR: Service ${serviceName} already exists.`, chalk.red);
|
|
4228
4280
|
process.exit(1);
|
|
4229
4281
|
}
|
|
4230
|
-
await
|
|
4282
|
+
await fs13.mkdirp(serviceDir);
|
|
4231
4283
|
const indexTs = getServiceIndexTemplate(serviceName, capitalize(serviceName));
|
|
4232
|
-
await
|
|
4284
|
+
await fs13.writeFile(serviceFile, indexTs);
|
|
4233
4285
|
logger.log(`\\nCreated new service: ${chalk.bold(serviceName)}`, chalk.green);
|
|
4234
4286
|
logger.log(` File: mcp/${serviceName}/index.ts`, chalk.gray);
|
|
4235
4287
|
logger.log(` Tool: greet`, chalk.gray);
|