@leanmcp/cli 0.4.5 → 0.5.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.js +594 -47
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,11 +3,11 @@ 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
|
|
8
|
-
import
|
|
6
|
+
import fs10 from "fs-extra";
|
|
7
|
+
import path10 from "path";
|
|
8
|
+
import ora8 from "ora";
|
|
9
9
|
import { createRequire } from "module";
|
|
10
|
-
import { confirm as
|
|
10
|
+
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
11
11
|
import { spawn as spawn4 } from "child_process";
|
|
12
12
|
|
|
13
13
|
// src/commands/dev.ts
|
|
@@ -1261,8 +1261,8 @@ __name(whoamiCommand, "whoamiCommand");
|
|
|
1261
1261
|
|
|
1262
1262
|
// src/commands/deploy.ts
|
|
1263
1263
|
import ora5 from "ora";
|
|
1264
|
-
import
|
|
1265
|
-
import
|
|
1264
|
+
import path8 from "path";
|
|
1265
|
+
import fs8 from "fs-extra";
|
|
1266
1266
|
import os3 from "os";
|
|
1267
1267
|
import archiver from "archiver";
|
|
1268
1268
|
import { input as input2, confirm as confirm2, select } from "@inquirer/prompts";
|
|
@@ -1444,6 +1444,128 @@ function generateProjectName() {
|
|
|
1444
1444
|
}
|
|
1445
1445
|
__name(generateProjectName, "generateProjectName");
|
|
1446
1446
|
|
|
1447
|
+
// src/utils/env-parser.ts
|
|
1448
|
+
import fs7 from "fs-extra";
|
|
1449
|
+
import path7 from "path";
|
|
1450
|
+
var RESERVED_ENV_KEYS = [
|
|
1451
|
+
"AWS_REGION",
|
|
1452
|
+
"AWS_ACCESS_KEY_ID",
|
|
1453
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
1454
|
+
"AWS_SESSION_TOKEN",
|
|
1455
|
+
"AWS_LAMBDA_FUNCTION_NAME",
|
|
1456
|
+
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE",
|
|
1457
|
+
"AWS_LAMBDA_FUNCTION_VERSION",
|
|
1458
|
+
"AWS_LAMBDA_LOG_GROUP_NAME",
|
|
1459
|
+
"AWS_LAMBDA_LOG_STREAM_NAME",
|
|
1460
|
+
"_HANDLER",
|
|
1461
|
+
"_X_AMZN_TRACE_ID"
|
|
1462
|
+
];
|
|
1463
|
+
var SYSTEM_ENV_KEYS = [
|
|
1464
|
+
"PORT",
|
|
1465
|
+
"AWS_LWA_PORT",
|
|
1466
|
+
"AWS_LWA_INVOKE_MODE",
|
|
1467
|
+
"AWS_LWA_READINESS_CHECK_MIN_UNHEALTHY_STATUS"
|
|
1468
|
+
];
|
|
1469
|
+
function parseEnvVar(input3) {
|
|
1470
|
+
if (!input3 || typeof input3 !== "string") {
|
|
1471
|
+
return null;
|
|
1472
|
+
}
|
|
1473
|
+
const trimmed = input3.trim();
|
|
1474
|
+
const equalIndex = trimmed.indexOf("=");
|
|
1475
|
+
if (equalIndex === -1) {
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1478
|
+
const key = trimmed.substring(0, equalIndex).trim();
|
|
1479
|
+
let value = trimmed.substring(equalIndex + 1);
|
|
1480
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1481
|
+
value = value.slice(1, -1);
|
|
1482
|
+
}
|
|
1483
|
+
if (!isValidEnvKey(key)) {
|
|
1484
|
+
return null;
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
key,
|
|
1488
|
+
value
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
__name(parseEnvVar, "parseEnvVar");
|
|
1492
|
+
function parseEnvFile(content) {
|
|
1493
|
+
const result = {};
|
|
1494
|
+
if (!content || typeof content !== "string") {
|
|
1495
|
+
return result;
|
|
1496
|
+
}
|
|
1497
|
+
const lines = content.split(/\r?\n/);
|
|
1498
|
+
for (const line of lines) {
|
|
1499
|
+
const trimmed = line.trim();
|
|
1500
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
const parsed = parseEnvVar(trimmed);
|
|
1504
|
+
if (parsed) {
|
|
1505
|
+
result[parsed.key] = parsed.value;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return result;
|
|
1509
|
+
}
|
|
1510
|
+
__name(parseEnvFile, "parseEnvFile");
|
|
1511
|
+
async function loadEnvFile(filePath) {
|
|
1512
|
+
const absolutePath = path7.resolve(filePath);
|
|
1513
|
+
if (!await fs7.pathExists(absolutePath)) {
|
|
1514
|
+
throw new Error(`Env file not found: ${absolutePath}`);
|
|
1515
|
+
}
|
|
1516
|
+
const content = await fs7.readFile(absolutePath, "utf-8");
|
|
1517
|
+
return parseEnvFile(content);
|
|
1518
|
+
}
|
|
1519
|
+
__name(loadEnvFile, "loadEnvFile");
|
|
1520
|
+
async function writeEnvFile(filePath, vars) {
|
|
1521
|
+
const absolutePath = path7.resolve(filePath);
|
|
1522
|
+
const lines = [
|
|
1523
|
+
"# Environment variables",
|
|
1524
|
+
`# Generated by leanmcp CLI at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
1525
|
+
""
|
|
1526
|
+
];
|
|
1527
|
+
const sortedKeys = Object.keys(vars).sort();
|
|
1528
|
+
for (const key of sortedKeys) {
|
|
1529
|
+
const value = vars[key];
|
|
1530
|
+
if (value.includes(" ") || value.includes("#") || value.includes('"')) {
|
|
1531
|
+
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
1532
|
+
} else {
|
|
1533
|
+
lines.push(`${key}=${value}`);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
lines.push("");
|
|
1537
|
+
await fs7.writeFile(absolutePath, lines.join("\n"));
|
|
1538
|
+
}
|
|
1539
|
+
__name(writeEnvFile, "writeEnvFile");
|
|
1540
|
+
function isValidEnvKey(key) {
|
|
1541
|
+
if (!key || typeof key !== "string") {
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1544
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
|
|
1545
|
+
}
|
|
1546
|
+
__name(isValidEnvKey, "isValidEnvKey");
|
|
1547
|
+
function isReservedKey(key) {
|
|
1548
|
+
return RESERVED_ENV_KEYS.includes(key);
|
|
1549
|
+
}
|
|
1550
|
+
__name(isReservedKey, "isReservedKey");
|
|
1551
|
+
function isSystemKey(key) {
|
|
1552
|
+
return SYSTEM_ENV_KEYS.includes(key);
|
|
1553
|
+
}
|
|
1554
|
+
__name(isSystemKey, "isSystemKey");
|
|
1555
|
+
function formatEnvVarsForDisplay(vars, reveal = false) {
|
|
1556
|
+
const keys = Object.keys(vars).sort();
|
|
1557
|
+
if (keys.length === 0) {
|
|
1558
|
+
return " (no environment variables)";
|
|
1559
|
+
}
|
|
1560
|
+
const lines = keys.map((key) => {
|
|
1561
|
+
const value = reveal ? vars[key] : "***";
|
|
1562
|
+
const keyType = isSystemKey(key) ? " (system)" : "";
|
|
1563
|
+
return ` ${key}=${value}${keyType}`;
|
|
1564
|
+
});
|
|
1565
|
+
return lines.join("\n");
|
|
1566
|
+
}
|
|
1567
|
+
__name(formatEnvVarsForDisplay, "formatEnvVarsForDisplay");
|
|
1568
|
+
|
|
1447
1569
|
// src/commands/deploy.ts
|
|
1448
1570
|
var DEBUG_MODE3 = false;
|
|
1449
1571
|
function setDeployDebugMode(enabled) {
|
|
@@ -1490,10 +1612,10 @@ var API_ENDPOINTS = {
|
|
|
1490
1612
|
var LEANMCP_CONFIG_DIR = ".leanmcp";
|
|
1491
1613
|
var LEANMCP_CONFIG_FILE = "config.json";
|
|
1492
1614
|
async function readLeanMCPConfig(projectPath) {
|
|
1493
|
-
const configPath =
|
|
1615
|
+
const configPath = path8.join(projectPath, LEANMCP_CONFIG_DIR, LEANMCP_CONFIG_FILE);
|
|
1494
1616
|
try {
|
|
1495
|
-
if (await
|
|
1496
|
-
const config = await
|
|
1617
|
+
if (await fs8.pathExists(configPath)) {
|
|
1618
|
+
const config = await fs8.readJSON(configPath);
|
|
1497
1619
|
debug3("Found existing .leanmcp config:", config);
|
|
1498
1620
|
return config;
|
|
1499
1621
|
}
|
|
@@ -1504,10 +1626,10 @@ async function readLeanMCPConfig(projectPath) {
|
|
|
1504
1626
|
}
|
|
1505
1627
|
__name(readLeanMCPConfig, "readLeanMCPConfig");
|
|
1506
1628
|
async function writeLeanMCPConfig(projectPath, config) {
|
|
1507
|
-
const configDir =
|
|
1508
|
-
const configPath =
|
|
1509
|
-
await
|
|
1510
|
-
await
|
|
1629
|
+
const configDir = path8.join(projectPath, LEANMCP_CONFIG_DIR);
|
|
1630
|
+
const configPath = path8.join(configDir, LEANMCP_CONFIG_FILE);
|
|
1631
|
+
await fs8.ensureDir(configDir);
|
|
1632
|
+
await fs8.writeJSON(configPath, config, {
|
|
1511
1633
|
spaces: 2
|
|
1512
1634
|
});
|
|
1513
1635
|
debug3("Saved .leanmcp config:", config);
|
|
@@ -1515,7 +1637,7 @@ async function writeLeanMCPConfig(projectPath, config) {
|
|
|
1515
1637
|
__name(writeLeanMCPConfig, "writeLeanMCPConfig");
|
|
1516
1638
|
async function createZipArchive(folderPath, outputPath) {
|
|
1517
1639
|
return new Promise((resolve, reject) => {
|
|
1518
|
-
const output =
|
|
1640
|
+
const output = fs8.createWriteStream(outputPath);
|
|
1519
1641
|
const archive = archiver("zip", {
|
|
1520
1642
|
zlib: {
|
|
1521
1643
|
level: 9
|
|
@@ -1613,13 +1735,13 @@ async function deployCommand(folderPath, options = {}) {
|
|
|
1613
1735
|
}
|
|
1614
1736
|
const apiUrl = await getApiUrl();
|
|
1615
1737
|
debug3("API URL:", apiUrl);
|
|
1616
|
-
const absolutePath =
|
|
1617
|
-
if (!await
|
|
1738
|
+
const absolutePath = path8.resolve(process.cwd(), folderPath);
|
|
1739
|
+
if (!await fs8.pathExists(absolutePath)) {
|
|
1618
1740
|
logger.error(`Folder not found: ${absolutePath}`);
|
|
1619
1741
|
process.exit(1);
|
|
1620
1742
|
}
|
|
1621
|
-
const hasMainTs = await
|
|
1622
|
-
const hasPackageJson = await
|
|
1743
|
+
const hasMainTs = await fs8.pathExists(path8.join(absolutePath, "main.ts"));
|
|
1744
|
+
const hasPackageJson = await fs8.pathExists(path8.join(absolutePath, "package.json"));
|
|
1623
1745
|
if (!hasMainTs && !hasPackageJson) {
|
|
1624
1746
|
logger.error("Not a valid project folder.");
|
|
1625
1747
|
logger.gray("Expected main.ts or package.json in the folder.\n");
|
|
@@ -1689,10 +1811,10 @@ Generated project name: ${chalk.bold(projectName)}
|
|
|
1689
1811
|
} catch (e) {
|
|
1690
1812
|
debug3("Could not fetch existing projects");
|
|
1691
1813
|
}
|
|
1692
|
-
let folderName =
|
|
1814
|
+
let folderName = path8.basename(absolutePath);
|
|
1693
1815
|
if (hasPackageJson) {
|
|
1694
1816
|
try {
|
|
1695
|
-
const pkg2 = await
|
|
1817
|
+
const pkg2 = await fs8.readJSON(path8.join(absolutePath, "package.json"));
|
|
1696
1818
|
folderName = pkg2.name || folderName;
|
|
1697
1819
|
} catch (e) {
|
|
1698
1820
|
}
|
|
@@ -1843,7 +1965,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
1843
1965
|
}
|
|
1844
1966
|
const uploadSpinner = ora5("Packaging and uploading...").start();
|
|
1845
1967
|
try {
|
|
1846
|
-
const tempZip =
|
|
1968
|
+
const tempZip = path8.join(os3.tmpdir(), `leanmcp-${Date.now()}.zip`);
|
|
1847
1969
|
const zipSize = await createZipArchive(absolutePath, tempZip);
|
|
1848
1970
|
uploadSpinner.text = `Packaging... (${Math.round(zipSize / 1024)}KB)`;
|
|
1849
1971
|
debug3("Step 2a: Getting upload URL for project:", projectId);
|
|
@@ -1870,7 +1992,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
1870
1992
|
throw new Error("Backend did not return upload URL");
|
|
1871
1993
|
}
|
|
1872
1994
|
debug3("Step 2b: Uploading to S3...");
|
|
1873
|
-
const zipBuffer = await
|
|
1995
|
+
const zipBuffer = await fs8.readFile(tempZip);
|
|
1874
1996
|
const s3Response = await fetch(uploadUrl, {
|
|
1875
1997
|
method: "PUT",
|
|
1876
1998
|
body: zipBuffer,
|
|
@@ -1893,7 +2015,7 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
1893
2015
|
s3Location
|
|
1894
2016
|
})
|
|
1895
2017
|
});
|
|
1896
|
-
await
|
|
2018
|
+
await fs8.remove(tempZip);
|
|
1897
2019
|
uploadSpinner.succeed("Project uploaded");
|
|
1898
2020
|
} catch (error) {
|
|
1899
2021
|
uploadSpinner.fail("Failed to upload");
|
|
@@ -2123,8 +2245,8 @@ async function projectsDeleteCommand(projectId, options = {}) {
|
|
|
2123
2245
|
process.exit(1);
|
|
2124
2246
|
}
|
|
2125
2247
|
if (!options.force) {
|
|
2126
|
-
const { confirm:
|
|
2127
|
-
const shouldDelete = await
|
|
2248
|
+
const { confirm: confirm5 } = await import("@inquirer/prompts");
|
|
2249
|
+
const shouldDelete = await confirm5({
|
|
2128
2250
|
message: `Are you sure you want to delete project ${projectId}?`,
|
|
2129
2251
|
default: false
|
|
2130
2252
|
});
|
|
@@ -2159,6 +2281,383 @@ ${error instanceof Error ? error.message : String(error)}`);
|
|
|
2159
2281
|
}
|
|
2160
2282
|
__name(projectsDeleteCommand, "projectsDeleteCommand");
|
|
2161
2283
|
|
|
2284
|
+
// src/commands/env.ts
|
|
2285
|
+
import ora7 from "ora";
|
|
2286
|
+
import path9 from "path";
|
|
2287
|
+
import fs9 from "fs-extra";
|
|
2288
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
2289
|
+
var DEBUG_MODE4 = false;
|
|
2290
|
+
function setEnvDebugMode(enabled) {
|
|
2291
|
+
DEBUG_MODE4 = enabled;
|
|
2292
|
+
}
|
|
2293
|
+
__name(setEnvDebugMode, "setEnvDebugMode");
|
|
2294
|
+
function debug4(message, ...args) {
|
|
2295
|
+
if (DEBUG_MODE4) {
|
|
2296
|
+
console.log(chalk.gray(`[DEBUG] ${message}`), ...args);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
__name(debug4, "debug");
|
|
2300
|
+
async function debugFetch2(url, options = {}) {
|
|
2301
|
+
debug4(`HTTP ${options.method || "GET"} ${url}`);
|
|
2302
|
+
if (options.body && typeof options.body === "string") {
|
|
2303
|
+
try {
|
|
2304
|
+
const body = JSON.parse(options.body);
|
|
2305
|
+
debug4("Request body:", JSON.stringify(body, null, 2));
|
|
2306
|
+
} catch {
|
|
2307
|
+
debug4("Request body:", options.body);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
const startTime = Date.now();
|
|
2311
|
+
const response = await fetch(url, options);
|
|
2312
|
+
const duration = Date.now() - startTime;
|
|
2313
|
+
debug4(`Response: ${response.status} ${response.statusText} (${duration}ms)`);
|
|
2314
|
+
return response;
|
|
2315
|
+
}
|
|
2316
|
+
__name(debugFetch2, "debugFetch");
|
|
2317
|
+
var LEANMCP_CONFIG_DIR2 = ".leanmcp";
|
|
2318
|
+
var LEANMCP_CONFIG_FILE2 = "config.json";
|
|
2319
|
+
async function readLeanMCPConfig2(projectPath) {
|
|
2320
|
+
const configPath = path9.join(projectPath, LEANMCP_CONFIG_DIR2, LEANMCP_CONFIG_FILE2);
|
|
2321
|
+
try {
|
|
2322
|
+
if (await fs9.pathExists(configPath)) {
|
|
2323
|
+
const config = await fs9.readJSON(configPath);
|
|
2324
|
+
debug4("Found existing .leanmcp config:", config);
|
|
2325
|
+
return config;
|
|
2326
|
+
}
|
|
2327
|
+
} catch (e) {
|
|
2328
|
+
debug4("Could not read .leanmcp config:", e);
|
|
2329
|
+
}
|
|
2330
|
+
return null;
|
|
2331
|
+
}
|
|
2332
|
+
__name(readLeanMCPConfig2, "readLeanMCPConfig");
|
|
2333
|
+
async function getDeploymentContext(folderPath) {
|
|
2334
|
+
const apiKey = await getApiKey();
|
|
2335
|
+
if (!apiKey) {
|
|
2336
|
+
logger.error("Not logged in.");
|
|
2337
|
+
logger.gray("Run `leanmcp login` first to authenticate.\n");
|
|
2338
|
+
return null;
|
|
2339
|
+
}
|
|
2340
|
+
const apiUrl = await getApiUrl();
|
|
2341
|
+
const absolutePath = path9.resolve(process.cwd(), folderPath);
|
|
2342
|
+
const config = await readLeanMCPConfig2(absolutePath);
|
|
2343
|
+
if (!config) {
|
|
2344
|
+
logger.error("No deployment found.");
|
|
2345
|
+
logger.gray(`No .leanmcp/config.json found in ${absolutePath}`);
|
|
2346
|
+
logger.gray("Deploy first with: leanmcp deploy .\n");
|
|
2347
|
+
return null;
|
|
2348
|
+
}
|
|
2349
|
+
if (!config.deploymentId) {
|
|
2350
|
+
logger.error("Deployment ID not found in config.");
|
|
2351
|
+
logger.gray("Please redeploy with: leanmcp deploy .\n");
|
|
2352
|
+
return null;
|
|
2353
|
+
}
|
|
2354
|
+
return {
|
|
2355
|
+
apiKey,
|
|
2356
|
+
apiUrl,
|
|
2357
|
+
config
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
__name(getDeploymentContext, "getDeploymentContext");
|
|
2361
|
+
async function envListCommand(folderPath, options = {}) {
|
|
2362
|
+
logger.info("\nLeanMCP Environment Variables\n");
|
|
2363
|
+
const context = await getDeploymentContext(folderPath);
|
|
2364
|
+
if (!context) {
|
|
2365
|
+
process.exit(1);
|
|
2366
|
+
}
|
|
2367
|
+
const { apiKey, apiUrl, config } = context;
|
|
2368
|
+
const spinner = ora7("Fetching environment variables...").start();
|
|
2369
|
+
try {
|
|
2370
|
+
const reveal = options.reveal ? "?reveal=true" : "";
|
|
2371
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env${reveal}`, {
|
|
2372
|
+
headers: {
|
|
2373
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2374
|
+
}
|
|
2375
|
+
});
|
|
2376
|
+
if (!response.ok) {
|
|
2377
|
+
const error = await response.text();
|
|
2378
|
+
throw new Error(`Failed to fetch env vars: ${error}`);
|
|
2379
|
+
}
|
|
2380
|
+
const envVars = await response.json();
|
|
2381
|
+
spinner.succeed("Environment variables retrieved");
|
|
2382
|
+
logger.info(`
|
|
2383
|
+
Project: ${config.projectName}`);
|
|
2384
|
+
logger.gray(`Deployment: ${config.deploymentId.substring(0, 8)}...`);
|
|
2385
|
+
logger.gray(`URL: ${config.url}
|
|
2386
|
+
`);
|
|
2387
|
+
const formatted = formatEnvVarsForDisplay(envVars, options.reveal);
|
|
2388
|
+
logger.log(formatted);
|
|
2389
|
+
logger.log("");
|
|
2390
|
+
if (!options.reveal) {
|
|
2391
|
+
logger.gray("Use --reveal to show actual values\n");
|
|
2392
|
+
}
|
|
2393
|
+
} catch (error) {
|
|
2394
|
+
spinner.fail("Failed to fetch environment variables");
|
|
2395
|
+
logger.error(`
|
|
2396
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2397
|
+
process.exit(1);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
__name(envListCommand, "envListCommand");
|
|
2401
|
+
async function envSetCommand(keyValue, folderPath, options = {}) {
|
|
2402
|
+
const context = await getDeploymentContext(folderPath);
|
|
2403
|
+
if (!context) {
|
|
2404
|
+
process.exit(1);
|
|
2405
|
+
}
|
|
2406
|
+
const { apiKey, apiUrl, config } = context;
|
|
2407
|
+
let variables = {};
|
|
2408
|
+
if (options.file) {
|
|
2409
|
+
const spinner2 = ora7(`Loading from ${options.file}...`).start();
|
|
2410
|
+
try {
|
|
2411
|
+
variables = await loadEnvFile(options.file);
|
|
2412
|
+
spinner2.succeed(`Loaded ${Object.keys(variables).length} variable(s) from ${options.file}`);
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
spinner2.fail(`Failed to load ${options.file}`);
|
|
2415
|
+
logger.error(`
|
|
2416
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2417
|
+
process.exit(1);
|
|
2418
|
+
}
|
|
2419
|
+
} else {
|
|
2420
|
+
const parsed = parseEnvVar(keyValue);
|
|
2421
|
+
if (!parsed) {
|
|
2422
|
+
logger.error("Invalid format. Expected: KEY=VALUE");
|
|
2423
|
+
logger.gray("Example: leanmcp env set API_KEY=secret123\n");
|
|
2424
|
+
process.exit(1);
|
|
2425
|
+
}
|
|
2426
|
+
if (isReservedKey(parsed.key)) {
|
|
2427
|
+
logger.error(`Cannot set reserved key: ${parsed.key}`);
|
|
2428
|
+
logger.gray("This key is managed by AWS Lambda.\n");
|
|
2429
|
+
process.exit(1);
|
|
2430
|
+
}
|
|
2431
|
+
if (isSystemKey(parsed.key) && !options.force) {
|
|
2432
|
+
logger.warn(`Warning: ${parsed.key} is a system key.`);
|
|
2433
|
+
const shouldContinue = await confirm3({
|
|
2434
|
+
message: "Are you sure you want to modify it?",
|
|
2435
|
+
default: false
|
|
2436
|
+
});
|
|
2437
|
+
if (!shouldContinue) {
|
|
2438
|
+
logger.gray("\nCancelled.\n");
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
variables = {
|
|
2443
|
+
[parsed.key]: parsed.value
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
if (Object.keys(variables).length === 0) {
|
|
2447
|
+
logger.warn("No variables to set.\n");
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
const spinner = ora7("Updating environment variables...").start();
|
|
2451
|
+
try {
|
|
2452
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env`, {
|
|
2453
|
+
method: "PUT",
|
|
2454
|
+
headers: {
|
|
2455
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
2456
|
+
"Content-Type": "application/json"
|
|
2457
|
+
},
|
|
2458
|
+
body: JSON.stringify({
|
|
2459
|
+
variables
|
|
2460
|
+
})
|
|
2461
|
+
});
|
|
2462
|
+
if (!response.ok) {
|
|
2463
|
+
const error = await response.text();
|
|
2464
|
+
throw new Error(`Failed to update env vars: ${error}`);
|
|
2465
|
+
}
|
|
2466
|
+
const result = await response.json();
|
|
2467
|
+
spinner.succeed("Environment variables updated");
|
|
2468
|
+
logger.info(`
|
|
2469
|
+
${result.message || "Variables updated successfully"}`);
|
|
2470
|
+
logger.gray("Note: Lambda will cold-start on next invocation.\n");
|
|
2471
|
+
} catch (error) {
|
|
2472
|
+
spinner.fail("Failed to update environment variables");
|
|
2473
|
+
logger.error(`
|
|
2474
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2475
|
+
process.exit(1);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
__name(envSetCommand, "envSetCommand");
|
|
2479
|
+
async function envGetCommand(key, folderPath, options = {}) {
|
|
2480
|
+
const context = await getDeploymentContext(folderPath);
|
|
2481
|
+
if (!context) {
|
|
2482
|
+
process.exit(1);
|
|
2483
|
+
}
|
|
2484
|
+
const { apiKey, apiUrl, config } = context;
|
|
2485
|
+
try {
|
|
2486
|
+
const reveal = options.reveal ? "?reveal=true" : "";
|
|
2487
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env${reveal}`, {
|
|
2488
|
+
headers: {
|
|
2489
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2492
|
+
if (!response.ok) {
|
|
2493
|
+
const error = await response.text();
|
|
2494
|
+
throw new Error(`Failed to fetch env vars: ${error}`);
|
|
2495
|
+
}
|
|
2496
|
+
const envVars = await response.json();
|
|
2497
|
+
if (key in envVars) {
|
|
2498
|
+
const value = envVars[key];
|
|
2499
|
+
logger.log(`${key}=${value}`);
|
|
2500
|
+
} else {
|
|
2501
|
+
logger.warn(`Variable '${key}' not found.`);
|
|
2502
|
+
process.exit(1);
|
|
2503
|
+
}
|
|
2504
|
+
} catch (error) {
|
|
2505
|
+
logger.error(`
|
|
2506
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
__name(envGetCommand, "envGetCommand");
|
|
2511
|
+
async function envRemoveCommand(key, folderPath, options = {}) {
|
|
2512
|
+
const context = await getDeploymentContext(folderPath);
|
|
2513
|
+
if (!context) {
|
|
2514
|
+
process.exit(1);
|
|
2515
|
+
}
|
|
2516
|
+
const { apiKey, apiUrl, config } = context;
|
|
2517
|
+
if (isReservedKey(key)) {
|
|
2518
|
+
logger.error(`Cannot remove reserved key: ${key}`);
|
|
2519
|
+
process.exit(1);
|
|
2520
|
+
}
|
|
2521
|
+
if (isSystemKey(key)) {
|
|
2522
|
+
logger.error(`Cannot remove system key: ${key}`);
|
|
2523
|
+
process.exit(1);
|
|
2524
|
+
}
|
|
2525
|
+
if (!options.force) {
|
|
2526
|
+
const shouldDelete = await confirm3({
|
|
2527
|
+
message: `Remove environment variable '${key}'?`,
|
|
2528
|
+
default: false
|
|
2529
|
+
});
|
|
2530
|
+
if (!shouldDelete) {
|
|
2531
|
+
logger.gray("\nCancelled.\n");
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
const spinner = ora7(`Removing ${key}...`).start();
|
|
2536
|
+
try {
|
|
2537
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env/${key}`, {
|
|
2538
|
+
method: "DELETE",
|
|
2539
|
+
headers: {
|
|
2540
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2541
|
+
}
|
|
2542
|
+
});
|
|
2543
|
+
if (!response.ok) {
|
|
2544
|
+
const error = await response.text();
|
|
2545
|
+
throw new Error(`Failed to remove env var: ${error}`);
|
|
2546
|
+
}
|
|
2547
|
+
spinner.succeed(`Removed ${key}`);
|
|
2548
|
+
logger.gray("Note: Lambda will cold-start on next invocation.\n");
|
|
2549
|
+
} catch (error) {
|
|
2550
|
+
spinner.fail(`Failed to remove ${key}`);
|
|
2551
|
+
logger.error(`
|
|
2552
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2553
|
+
process.exit(1);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
__name(envRemoveCommand, "envRemoveCommand");
|
|
2557
|
+
async function envPullCommand(folderPath, options = {}) {
|
|
2558
|
+
const context = await getDeploymentContext(folderPath);
|
|
2559
|
+
if (!context) {
|
|
2560
|
+
process.exit(1);
|
|
2561
|
+
}
|
|
2562
|
+
const { apiKey, apiUrl, config } = context;
|
|
2563
|
+
const outputFile = options.file || ".env.remote";
|
|
2564
|
+
const spinner = ora7("Fetching environment variables...").start();
|
|
2565
|
+
try {
|
|
2566
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env?reveal=true`, {
|
|
2567
|
+
headers: {
|
|
2568
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
if (!response.ok) {
|
|
2572
|
+
const error = await response.text();
|
|
2573
|
+
throw new Error(`Failed to fetch env vars: ${error}`);
|
|
2574
|
+
}
|
|
2575
|
+
const envVars = await response.json();
|
|
2576
|
+
const userVars = {};
|
|
2577
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
2578
|
+
if (!isSystemKey(key)) {
|
|
2579
|
+
userVars[key] = value;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
spinner.text = `Writing to ${outputFile}...`;
|
|
2583
|
+
await writeEnvFile(outputFile, userVars);
|
|
2584
|
+
spinner.succeed(`Saved ${Object.keys(userVars).length} variable(s) to ${outputFile}`);
|
|
2585
|
+
logger.gray(`
|
|
2586
|
+
System variables (PORT, AWS_LWA_*) are not included.
|
|
2587
|
+
`);
|
|
2588
|
+
} catch (error) {
|
|
2589
|
+
spinner.fail("Failed to pull environment variables");
|
|
2590
|
+
logger.error(`
|
|
2591
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2592
|
+
process.exit(1);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
__name(envPullCommand, "envPullCommand");
|
|
2596
|
+
async function envPushCommand(folderPath, options = {}) {
|
|
2597
|
+
const context = await getDeploymentContext(folderPath);
|
|
2598
|
+
if (!context) {
|
|
2599
|
+
process.exit(1);
|
|
2600
|
+
}
|
|
2601
|
+
const { apiKey, apiUrl, config } = context;
|
|
2602
|
+
const inputFile = options.file || ".env";
|
|
2603
|
+
const loadSpinner = ora7(`Loading from ${inputFile}...`).start();
|
|
2604
|
+
let variables;
|
|
2605
|
+
try {
|
|
2606
|
+
variables = await loadEnvFile(inputFile);
|
|
2607
|
+
loadSpinner.succeed(`Loaded ${Object.keys(variables).length} variable(s) from ${inputFile}`);
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
loadSpinner.fail(`Failed to load ${inputFile}`);
|
|
2610
|
+
logger.error(`
|
|
2611
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2612
|
+
process.exit(1);
|
|
2613
|
+
}
|
|
2614
|
+
if (Object.keys(variables).length === 0) {
|
|
2615
|
+
logger.warn("No variables found in file.\n");
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
logger.warn("\nThis will REPLACE ALL current environment variables.");
|
|
2619
|
+
logger.gray("System variables (PORT, AWS_LWA_*) will be preserved.\n");
|
|
2620
|
+
if (!options.force) {
|
|
2621
|
+
const shouldPush = await confirm3({
|
|
2622
|
+
message: `Replace all env vars with ${Object.keys(variables).length} variables from ${inputFile}?`,
|
|
2623
|
+
default: false
|
|
2624
|
+
});
|
|
2625
|
+
if (!shouldPush) {
|
|
2626
|
+
logger.gray("\nCancelled.\n");
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
const pushSpinner = ora7("Pushing environment variables...").start();
|
|
2631
|
+
try {
|
|
2632
|
+
const response = await debugFetch2(`${apiUrl}/api/lambda-deploy/${config.deploymentId}/env`, {
|
|
2633
|
+
method: "PUT",
|
|
2634
|
+
headers: {
|
|
2635
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
2636
|
+
"Content-Type": "application/json"
|
|
2637
|
+
},
|
|
2638
|
+
body: JSON.stringify({
|
|
2639
|
+
variables,
|
|
2640
|
+
replaceAll: true
|
|
2641
|
+
})
|
|
2642
|
+
});
|
|
2643
|
+
if (!response.ok) {
|
|
2644
|
+
const error = await response.text();
|
|
2645
|
+
throw new Error(`Failed to push env vars: ${error}`);
|
|
2646
|
+
}
|
|
2647
|
+
const result = await response.json();
|
|
2648
|
+
pushSpinner.succeed("Environment variables pushed");
|
|
2649
|
+
logger.info(`
|
|
2650
|
+
${result.message || "Variables updated successfully"}`);
|
|
2651
|
+
logger.gray("Note: Lambda will cold-start on next invocation.\n");
|
|
2652
|
+
} catch (error) {
|
|
2653
|
+
pushSpinner.fail("Failed to push environment variables");
|
|
2654
|
+
logger.error(`
|
|
2655
|
+
${error instanceof Error ? error.message : String(error)}`);
|
|
2656
|
+
process.exit(1);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
__name(envPushCommand, "envPushCommand");
|
|
2660
|
+
|
|
2162
2661
|
// src/templates/readme_v1.ts
|
|
2163
2662
|
var getReadmeTemplate = /* @__PURE__ */ __name((projectName) => `<p align="center">
|
|
2164
2663
|
<img
|
|
@@ -2675,6 +3174,7 @@ function enableDebugIfNeeded() {
|
|
|
2675
3174
|
setDebugMode(true);
|
|
2676
3175
|
setDebugMode2(true);
|
|
2677
3176
|
setDeployDebugMode(true);
|
|
3177
|
+
setEnvDebugMode(true);
|
|
2678
3178
|
debug("Debug mode enabled globally");
|
|
2679
3179
|
}
|
|
2680
3180
|
}
|
|
@@ -2692,6 +3192,8 @@ Examples:
|
|
|
2692
3192
|
$ leanmcp deploy ./my-app # Deploy to LeanMCP cloud
|
|
2693
3193
|
$ leanmcp projects list # List your cloud projects
|
|
2694
3194
|
$ leanmcp projects delete <id> # Delete a cloud project
|
|
3195
|
+
$ leanmcp env list # List environment variables
|
|
3196
|
+
$ leanmcp env set KEY=VALUE # Set an environment variable
|
|
2695
3197
|
|
|
2696
3198
|
Global Options:
|
|
2697
3199
|
-v, --version Output the current version
|
|
@@ -2703,14 +3205,14 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
2703
3205
|
projectName,
|
|
2704
3206
|
...options
|
|
2705
3207
|
});
|
|
2706
|
-
const spinner =
|
|
2707
|
-
const targetDir =
|
|
2708
|
-
if (
|
|
3208
|
+
const spinner = ora8(`Creating project ${projectName}...`).start();
|
|
3209
|
+
const targetDir = path10.join(process.cwd(), projectName);
|
|
3210
|
+
if (fs10.existsSync(targetDir)) {
|
|
2709
3211
|
spinner.fail(`Folder ${projectName} already exists.`);
|
|
2710
3212
|
process.exit(1);
|
|
2711
3213
|
}
|
|
2712
|
-
await
|
|
2713
|
-
await
|
|
3214
|
+
await fs10.mkdirp(targetDir);
|
|
3215
|
+
await fs10.mkdirp(path10.join(targetDir, "mcp", "example"));
|
|
2714
3216
|
const pkg2 = {
|
|
2715
3217
|
name: projectName,
|
|
2716
3218
|
version: "1.0.0",
|
|
@@ -2746,7 +3248,7 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
2746
3248
|
"typescript": "^5.6.3"
|
|
2747
3249
|
}
|
|
2748
3250
|
};
|
|
2749
|
-
await
|
|
3251
|
+
await fs10.writeJSON(path10.join(targetDir, "package.json"), pkg2, {
|
|
2750
3252
|
spaces: 2
|
|
2751
3253
|
});
|
|
2752
3254
|
const tsconfig = {
|
|
@@ -2769,15 +3271,15 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
2769
3271
|
"dist"
|
|
2770
3272
|
]
|
|
2771
3273
|
};
|
|
2772
|
-
await
|
|
3274
|
+
await fs10.writeJSON(path10.join(targetDir, "tsconfig.json"), tsconfig, {
|
|
2773
3275
|
spaces: 2
|
|
2774
3276
|
});
|
|
2775
3277
|
const dashboardLine = options.dashboard === false ? `
|
|
2776
3278
|
dashboard: false, // Dashboard disabled via --no-dashboard` : "";
|
|
2777
3279
|
const mainTs = getMainTsTemplate(projectName, dashboardLine);
|
|
2778
|
-
await
|
|
3280
|
+
await fs10.writeFile(path10.join(targetDir, "main.ts"), mainTs);
|
|
2779
3281
|
const exampleServiceTs = getExampleServiceTemplate(projectName);
|
|
2780
|
-
await
|
|
3282
|
+
await fs10.writeFile(path10.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
|
|
2781
3283
|
const gitignore = gitignoreTemplate;
|
|
2782
3284
|
const env = `# Server Configuration
|
|
2783
3285
|
PORT=3001
|
|
@@ -2785,10 +3287,10 @@ NODE_ENV=development
|
|
|
2785
3287
|
|
|
2786
3288
|
# Add your environment variables here
|
|
2787
3289
|
`;
|
|
2788
|
-
await
|
|
2789
|
-
await
|
|
3290
|
+
await fs10.writeFile(path10.join(targetDir, ".gitignore"), gitignore);
|
|
3291
|
+
await fs10.writeFile(path10.join(targetDir, ".env"), env);
|
|
2790
3292
|
const readme = getReadmeTemplate(projectName);
|
|
2791
|
-
await
|
|
3293
|
+
await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
|
|
2792
3294
|
spinner.succeed(`Project ${projectName} created!`);
|
|
2793
3295
|
logger.log("\nSuccess! Your MCP server is ready.\n", chalk.green);
|
|
2794
3296
|
logger.log("To deploy to LeanMCP cloud:", chalk.cyan);
|
|
@@ -2809,12 +3311,12 @@ NODE_ENV=development
|
|
|
2809
3311
|
logger.log(` leanmcp deploy .`, chalk.gray);
|
|
2810
3312
|
return;
|
|
2811
3313
|
}
|
|
2812
|
-
const shouldInstall = isNonInteractive ? true : await
|
|
3314
|
+
const shouldInstall = isNonInteractive ? true : await confirm4({
|
|
2813
3315
|
message: "Would you like to install dependencies now?",
|
|
2814
3316
|
default: true
|
|
2815
3317
|
});
|
|
2816
3318
|
if (shouldInstall) {
|
|
2817
|
-
const installSpinner =
|
|
3319
|
+
const installSpinner = ora8("Installing dependencies...").start();
|
|
2818
3320
|
try {
|
|
2819
3321
|
await new Promise((resolve, reject) => {
|
|
2820
3322
|
const npmInstall = spawn4("npm", [
|
|
@@ -2844,7 +3346,7 @@ NODE_ENV=development
|
|
|
2844
3346
|
logger.log(` leanmcp deploy .`, chalk.gray);
|
|
2845
3347
|
return;
|
|
2846
3348
|
}
|
|
2847
|
-
const shouldStartDev = options.allowAll ? true : await
|
|
3349
|
+
const shouldStartDev = options.allowAll ? true : await confirm4({
|
|
2848
3350
|
message: "Would you like to start the development server?",
|
|
2849
3351
|
default: true
|
|
2850
3352
|
});
|
|
@@ -2891,20 +3393,20 @@ NODE_ENV=development
|
|
|
2891
3393
|
});
|
|
2892
3394
|
program.command("add <serviceName>").description("Add a new MCP service to your project").action(async (serviceName) => {
|
|
2893
3395
|
const cwd = process.cwd();
|
|
2894
|
-
const mcpDir =
|
|
2895
|
-
if (!
|
|
3396
|
+
const mcpDir = path10.join(cwd, "mcp");
|
|
3397
|
+
if (!fs10.existsSync(path10.join(cwd, "main.ts"))) {
|
|
2896
3398
|
logger.log("ERROR: Not a LeanMCP project (main.ts missing).", chalk.red);
|
|
2897
3399
|
process.exit(1);
|
|
2898
3400
|
}
|
|
2899
|
-
const serviceDir =
|
|
2900
|
-
const serviceFile =
|
|
2901
|
-
if (
|
|
3401
|
+
const serviceDir = path10.join(mcpDir, serviceName);
|
|
3402
|
+
const serviceFile = path10.join(serviceDir, "index.ts");
|
|
3403
|
+
if (fs10.existsSync(serviceDir)) {
|
|
2902
3404
|
logger.log(`ERROR: Service ${serviceName} already exists.`, chalk.red);
|
|
2903
3405
|
process.exit(1);
|
|
2904
3406
|
}
|
|
2905
|
-
await
|
|
3407
|
+
await fs10.mkdirp(serviceDir);
|
|
2906
3408
|
const indexTs = getServiceIndexTemplate(serviceName, capitalize(serviceName));
|
|
2907
|
-
await
|
|
3409
|
+
await fs10.writeFile(serviceFile, indexTs);
|
|
2908
3410
|
logger.log(`\\nCreated new service: ${chalk.bold(serviceName)}`, chalk.green);
|
|
2909
3411
|
logger.log(` File: mcp/${serviceName}/index.ts`, chalk.gray);
|
|
2910
3412
|
logger.log(` Tool: greet`, chalk.gray);
|
|
@@ -2966,4 +3468,49 @@ projectsCmd.command("delete <projectId>").alias("rm").description("Delete a proj
|
|
|
2966
3468
|
});
|
|
2967
3469
|
projectsDeleteCommand(projectId, options);
|
|
2968
3470
|
});
|
|
3471
|
+
var envCmd = program.command("env").description("Manage environment variables for deployed projects");
|
|
3472
|
+
envCmd.command("list [folder]").alias("ls").description("List all environment variables").option("--reveal", "Show actual values instead of masked").option("--project-id <id>", "Specify project ID").action((folder, options) => {
|
|
3473
|
+
trackCommand("env_list", {
|
|
3474
|
+
folder,
|
|
3475
|
+
...options
|
|
3476
|
+
});
|
|
3477
|
+
envListCommand(folder || ".", options);
|
|
3478
|
+
});
|
|
3479
|
+
envCmd.command("set <keyValue> [folder]").description("Set an environment variable (KEY=VALUE)").option("-f, --file <file>", "Load from env file").option("--force", "Skip confirmation for reserved keys").action((keyValue, folder, options) => {
|
|
3480
|
+
trackCommand("env_set", {
|
|
3481
|
+
folder,
|
|
3482
|
+
...options
|
|
3483
|
+
});
|
|
3484
|
+
envSetCommand(keyValue, folder || ".", options);
|
|
3485
|
+
});
|
|
3486
|
+
envCmd.command("get <key> [folder]").description("Get an environment variable value").option("--reveal", "Show actual value").action((key, folder, options) => {
|
|
3487
|
+
trackCommand("env_get", {
|
|
3488
|
+
key,
|
|
3489
|
+
folder,
|
|
3490
|
+
...options
|
|
3491
|
+
});
|
|
3492
|
+
envGetCommand(key, folder || ".", options);
|
|
3493
|
+
});
|
|
3494
|
+
envCmd.command("remove <key> [folder]").alias("rm").description("Remove an environment variable").option("--force", "Skip confirmation").action((key, folder, options) => {
|
|
3495
|
+
trackCommand("env_remove", {
|
|
3496
|
+
key,
|
|
3497
|
+
folder,
|
|
3498
|
+
...options
|
|
3499
|
+
});
|
|
3500
|
+
envRemoveCommand(key, folder || ".", options);
|
|
3501
|
+
});
|
|
3502
|
+
envCmd.command("pull [folder]").description("Download environment variables to local .env file").option("-f, --file <file>", "Output file", ".env.remote").action((folder, options) => {
|
|
3503
|
+
trackCommand("env_pull", {
|
|
3504
|
+
folder,
|
|
3505
|
+
...options
|
|
3506
|
+
});
|
|
3507
|
+
envPullCommand(folder || ".", options);
|
|
3508
|
+
});
|
|
3509
|
+
envCmd.command("push [folder]").description("Upload environment variables from local .env file (replaces all)").option("-f, --file <file>", "Input file", ".env").option("--force", "Skip confirmation").action((folder, options) => {
|
|
3510
|
+
trackCommand("env_push", {
|
|
3511
|
+
folder,
|
|
3512
|
+
...options
|
|
3513
|
+
});
|
|
3514
|
+
envPushCommand(folder || ".", options);
|
|
3515
|
+
});
|
|
2969
3516
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leanmcp/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Command-line interface for scaffolding LeanMCP projects",
|
|
5
5
|
"bin": {
|
|
6
6
|
"leanmcp": "bin/leanmcp.js"
|
|
@@ -70,4 +70,4 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}
|