@enactprotocol/mcp-server 1.2.4 → 1.2.5

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 (3) hide show
  1. package/dist/index.js +626 -574
  2. package/dist/index.js.bak +626 -574
  3. package/package.json +2 -5
package/dist/index.js CHANGED
@@ -235541,6 +235541,49 @@ class DaggerExecutionProvider extends ExecutionProvider {
235541
235541
  });
235542
235542
  });
235543
235543
  }
235544
+ async setupDirectoryMount(client, container, mountSpec) {
235545
+ try {
235546
+ let localPath2;
235547
+ let containerPath;
235548
+ const colonIndex = mountSpec.indexOf(":");
235549
+ if (colonIndex > 0) {
235550
+ const potentialDriveLetter = mountSpec.substring(0, colonIndex);
235551
+ const isWindowsDrive = potentialDriveLetter.length === 1 && /[A-Za-z]/.test(potentialDriveLetter);
235552
+ if (isWindowsDrive) {
235553
+ const nextColonIndex = mountSpec.indexOf(":", colonIndex + 1);
235554
+ if (nextColonIndex > 0) {
235555
+ localPath2 = mountSpec.substring(0, nextColonIndex);
235556
+ containerPath = mountSpec.substring(nextColonIndex + 1);
235557
+ } else {
235558
+ localPath2 = mountSpec;
235559
+ containerPath = "/workspace/src";
235560
+ }
235561
+ } else {
235562
+ localPath2 = mountSpec.substring(0, colonIndex);
235563
+ containerPath = mountSpec.substring(colonIndex + 1);
235564
+ }
235565
+ } else if (colonIndex === 0) {
235566
+ localPath2 = "";
235567
+ containerPath = mountSpec.substring(1);
235568
+ } else {
235569
+ localPath2 = mountSpec;
235570
+ containerPath = "/workspace/src";
235571
+ }
235572
+ const path7 = __require("path");
235573
+ const resolvedLocalPath = path7.resolve(localPath2);
235574
+ const fs4 = __require("fs");
235575
+ if (!fs4.existsSync(resolvedLocalPath)) {
235576
+ throw new Error(`Mount source directory does not exist: ${resolvedLocalPath}`);
235577
+ }
235578
+ const hostDirectory = client.host().directory(resolvedLocalPath);
235579
+ container = container.withMountedDirectory(containerPath, hostDirectory);
235580
+ logger_default.debug(`\uD83D\uDCC2 Mounted ${resolvedLocalPath} -> ${containerPath}`);
235581
+ return container;
235582
+ } catch (error) {
235583
+ logger_default.error(`Failed to setup directory mount: ${error}`);
235584
+ throw error;
235585
+ }
235586
+ }
235544
235587
  async setupContainer(client, environment, inputs, tool) {
235545
235588
  const containerImage = tool?.from || this.options.baseImage;
235546
235589
  logger_default.debug(`\uD83D\uDE80 Setting up container with image: ${containerImage}${tool?.from ? " (from tool.from)" : " (default baseImage)"}`);
@@ -235548,6 +235591,9 @@ class DaggerExecutionProvider extends ExecutionProvider {
235548
235591
  logger_default.debug("\uD83D\uDCE6 Base container created");
235549
235592
  container = container.withWorkdir(this.options.workdir);
235550
235593
  logger_default.debug(`\uD83D\uDCC1 Working directory set to: ${this.options.workdir}`);
235594
+ if (environment.mount) {
235595
+ container = await this.setupDirectoryMount(client, container, environment.mount);
235596
+ }
235551
235597
  for (const [key, value] of Object.entries(environment.vars)) {
235552
235598
  container = container.withEnvVariable(key, String(value));
235553
235599
  }
@@ -238995,7 +239041,6 @@ class EnactCore {
238995
239041
  const messageHash = CryptoUtils.hash(docString);
238996
239042
  const directVerify = CryptoUtils.verify(referenceSignature.publicKey, messageHash, referenceSignature.signature);
238997
239043
  const isValid2 = SigningService.verifyDocument(documentForVerification, referenceSignature, { includeFields: ["command"] });
238998
- console.log("Final verification result:", isValid2);
238999
239044
  if (!isValid2) {
239000
239045
  throw new Error(`Tool ${tool.name} has invalid signatures`);
239001
239046
  }
@@ -239025,7 +239070,8 @@ class EnactCore {
239025
239070
  vars: { ...envVars, ...validatedInputs },
239026
239071
  resources: {
239027
239072
  timeout: options.timeout || tool.timeout || this.options.defaultTimeout
239028
- }
239073
+ },
239074
+ mount: options.mount
239029
239075
  });
239030
239076
  } catch (error) {
239031
239077
  return {
@@ -239181,577 +239227,154 @@ class EnactCore {
239181
239227
  return `exec_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
239182
239228
  }
239183
239229
  }
239184
- // ../shared/dist/utils/help.js
239185
- var __dirname = "/Users/keithgroves/projects/enact/enact-cli/packages/shared/dist/utils";
239186
- // ../shared/dist/utils/logger.js
239187
- var LogLevel;
239188
- (function(LogLevel2) {
239189
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
239190
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
239191
- LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
239192
- LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
239193
- LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
239194
- })(LogLevel || (LogLevel = {}));
239195
- var currentLogLevel = LogLevel.INFO;
239196
- // ../shared/dist/utils/silent-monitor.js
239197
- class McpSilentOperationMonitor {
239198
- getOriginalConsoleError() {
239199
- return this.originalConsoleError;
239200
- }
239201
- constructor() {
239202
- this.isMonitoring = false;
239203
- this.violations = [];
239204
- this.consoleOutput = [];
239205
- this.processExitAttempts = 0;
239206
- this.readlineUsageDetected = false;
239207
- this.startTime = 0;
239208
- this.originalConsoleLog = console.log;
239209
- this.originalConsoleError = console.error;
239210
- this.originalConsoleWarn = console.warn;
239211
- this.originalConsoleInfo = console.info;
239212
- this.originalProcessExit = process.exit;
239213
- this.originalStdoutWrite = process.stdout.write;
239214
- this.originalStderrWrite = process.stderr.write;
239230
+ // ../shared/dist/LocalToolResolver.js
239231
+ var yaml2 = __toESM(require_dist3(), 1);
239232
+ import { promises as fs5, readFileSync as readFileSync3, writeFileSync } from "fs";
239233
+ import { join as join4, resolve, basename as basename2 } from "path";
239234
+ class LocalToolResolver {
239235
+ constructor(enactCore, localToolsDir = "./tools", cacheDir = "./.tool-cache") {
239236
+ this.enactCore = enactCore;
239237
+ this.toolCache = new Map;
239238
+ this.aliases = new Map;
239239
+ this.favorites = new Set;
239240
+ this.localToolsDir = resolve(localToolsDir);
239241
+ this.cacheDir = resolve(cacheDir);
239242
+ this.loadConfiguration();
239215
239243
  }
239216
- startMonitoring() {
239217
- if (this.isMonitoring) {
239218
- return;
239244
+ async resolveTool(toolName) {
239245
+ const resolvedName = this.aliases.get(toolName) || toolName;
239246
+ const localTool = await this.getLocalTool(resolvedName);
239247
+ if (localTool) {
239248
+ return {
239249
+ tool: localTool,
239250
+ source: "local",
239251
+ metadata: { path: localTool.path }
239252
+ };
239219
239253
  }
239220
- this.isMonitoring = true;
239221
- this.violations = [];
239222
- this.consoleOutput = [];
239223
- this.processExitAttempts = 0;
239224
- this.readlineUsageDetected = false;
239225
- this.startTime = Date.now();
239226
- console.log = (...args) => {
239227
- const message = args.join(" ");
239228
- this.consoleOutput.push(`[LOG] ${message}`);
239229
- this.violations.push(`Console.log called: ${message}`);
239230
- };
239231
- console.error = (...args) => {
239232
- const message = args.join(" ");
239233
- this.consoleOutput.push(`[ERROR] ${message}`);
239234
- this.violations.push(`Console.error called: ${message}`);
239235
- };
239236
- console.warn = (...args) => {
239237
- const message = args.join(" ");
239238
- this.consoleOutput.push(`[WARN] ${message}`);
239239
- this.violations.push(`Console.warn called: ${message}`);
239240
- };
239241
- console.info = (...args) => {
239242
- const message = args.join(" ");
239243
- this.consoleOutput.push(`[INFO] ${message}`);
239244
- this.violations.push(`Console.info called: ${message}`);
239245
- };
239246
- process.exit = (code) => {
239247
- this.processExitAttempts++;
239248
- this.violations.push(`Process.exit called with code: ${code}`);
239249
- throw new Error(`Process exit intercepted: ${code}`);
239250
- };
239251
- process.stdout.write = (chunk, ...args) => {
239252
- if (typeof chunk === "string" && chunk.trim()) {
239253
- this.consoleOutput.push(`[STDOUT] ${chunk}`);
239254
- this.violations.push(`Process.stdout.write called: ${chunk.substring(0, 100)}...`);
239255
- }
239256
- return true;
239257
- };
239258
- process.stderr.write = (chunk, ...args) => {
239259
- if (typeof chunk === "string" && chunk.trim()) {
239260
- this.consoleOutput.push(`[STDERR] ${chunk}`);
239261
- this.violations.push(`Process.stderr.write called: ${chunk.substring(0, 100)}...`);
239262
- }
239263
- return true;
239264
- };
239265
- const originalRequire = __require;
239266
- const monitor = this;
239267
- global.require = function(id) {
239268
- if (id === "readline" || id.includes("readline")) {
239269
- monitor.readlineUsageDetected = true;
239270
- monitor.violations.push("Readline module usage detected");
239254
+ const cachedTool = this.toolCache.get(resolvedName);
239255
+ if (cachedTool && !this.isCacheExpired(cachedTool)) {
239256
+ return {
239257
+ tool: cachedTool,
239258
+ source: "cache",
239259
+ metadata: { cachedAt: cachedTool.lastModified }
239260
+ };
239261
+ }
239262
+ try {
239263
+ const registryTool = await this.enactCore.getToolByName(resolvedName);
239264
+ if (registryTool) {
239265
+ await this.cacheRegistryTool(resolvedName, registryTool);
239266
+ return {
239267
+ tool: registryTool,
239268
+ source: "registry",
239269
+ metadata: { cached: true }
239270
+ };
239271
239271
  }
239272
- return originalRequire.apply(this, arguments);
239273
- };
239274
- }
239275
- stopMonitoring() {
239276
- if (!this.isMonitoring) {
239277
- throw new Error("Monitor is not currently running");
239272
+ } catch (error) {
239273
+ logger_default.debug(`Registry lookup failed for ${resolvedName}:`, error);
239278
239274
  }
239279
- console.log = this.originalConsoleLog;
239280
- console.error = this.originalConsoleError;
239281
- console.warn = this.originalConsoleWarn;
239282
- console.info = this.originalConsoleInfo;
239283
- process.exit = this.originalProcessExit;
239284
- process.stdout.write = this.originalStdoutWrite;
239285
- process.stderr.write = this.originalStderrWrite;
239286
- global.require = __require;
239287
- this.isMonitoring = false;
239288
- return {
239289
- violations: [...this.violations],
239290
- consoleOutputDetected: [...this.consoleOutput],
239291
- processExitAttempts: this.processExitAttempts,
239292
- readlineUsageDetected: this.readlineUsageDetected,
239293
- duration: Date.now() - this.startTime,
239294
- timestamp: new Date().toISOString()
239295
- };
239275
+ return null;
239296
239276
  }
239297
- isCurrentlyMonitoring() {
239298
- return this.isMonitoring;
239277
+ async getLocalTool(toolName) {
239278
+ const possiblePaths = [
239279
+ join4(this.localToolsDir, `${toolName}.yaml`),
239280
+ join4(this.localToolsDir, `${toolName}.yml`),
239281
+ join4(this.localToolsDir, toolName, "tool.yaml"),
239282
+ join4(this.localToolsDir, toolName, "tool.yml"),
239283
+ join4(this.localToolsDir, toolName, `${toolName}.yaml`),
239284
+ join4(this.localToolsDir, toolName, `${toolName}.yml`)
239285
+ ];
239286
+ for (const toolPath of possiblePaths) {
239287
+ try {
239288
+ const stats = await fs5.stat(toolPath);
239289
+ const content = await fs5.readFile(toolPath, "utf-8");
239290
+ const definition = yaml2.parse(content);
239291
+ if (definition && (definition.name === toolName || definition.name === undefined)) {
239292
+ return {
239293
+ name: definition.name || toolName,
239294
+ path: toolPath,
239295
+ definition,
239296
+ lastModified: stats.mtime,
239297
+ cached: false
239298
+ };
239299
+ }
239300
+ } catch (error) {
239301
+ continue;
239302
+ }
239303
+ }
239304
+ return null;
239299
239305
  }
239300
- getViolations() {
239301
- return [...this.violations];
239306
+ async cacheRegistryTool(toolName, tool) {
239307
+ try {
239308
+ await fs5.mkdir(this.cacheDir, { recursive: true });
239309
+ const cachePath = join4(this.cacheDir, `${toolName}.yaml`);
239310
+ const cacheData = {
239311
+ ...tool,
239312
+ _cached: true,
239313
+ _cachedAt: new Date().toISOString(),
239314
+ _source: "registry"
239315
+ };
239316
+ await fs5.writeFile(cachePath, yaml2.stringify(cacheData));
239317
+ this.toolCache.set(toolName, {
239318
+ name: toolName,
239319
+ path: cachePath,
239320
+ definition: tool,
239321
+ lastModified: new Date,
239322
+ cached: true
239323
+ });
239324
+ logger_default.debug(`Cached registry tool: ${toolName}`);
239325
+ } catch (error) {
239326
+ logger_default.warn(`Failed to cache tool ${toolName}:`, error);
239327
+ }
239302
239328
  }
239303
- }
239304
- var globalMonitor = new McpSilentOperationMonitor;
239305
- function validateSilentEnvironment() {
239306
- const issues = [];
239307
- if (process.env.CI !== "true") {
239308
- issues.push('CI environment variable not set to "true"');
239329
+ isCacheExpired(tool, maxAge = 24 * 60 * 60 * 1000) {
239330
+ return Date.now() - tool.lastModified.getTime() > maxAge;
239309
239331
  }
239310
- if (process.env.ENACT_SKIP_INTERACTIVE !== "true") {
239311
- issues.push('ENACT_SKIP_INTERACTIVE not set to "true"');
239332
+ async listAllTools() {
239333
+ const localTools = await this.scanLocalTools();
239334
+ const cachedTools = Array.from(this.toolCache.values());
239335
+ return {
239336
+ local: localTools,
239337
+ cached: cachedTools,
239338
+ favorites: Array.from(this.favorites),
239339
+ aliases: Object.fromEntries(this.aliases)
239340
+ };
239312
239341
  }
239313
- if (process.env.DEBUG === "true" || process.env.VERBOSE === "true") {
239314
- issues.push("DEBUG or VERBOSE environment variables are enabled");
239315
- }
239316
- if (process.stdin.isTTY) {
239317
- issues.push("Process is running in TTY mode (potentially interactive)");
239318
- }
239319
- return {
239320
- valid: issues.length === 0,
239321
- issues
239322
- };
239323
- }
239324
- // ../shared/dist/utils/version.js
239325
- var __filename = "/Users/keithgroves/projects/enact/enact-cli/packages/shared/dist/utils/version.js";
239326
- // ../shared/dist/web/env-manager-server.js
239327
- import { createServer } from "http";
239328
- import { parse as parse3 } from "url";
239329
- import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, readdir, stat as stat2 } from "fs/promises";
239330
- import { existsSync as existsSync4 } from "fs";
239331
- import { join as join4, dirname as dirname3 } from "path";
239332
- import { homedir as homedir4 } from "os";
239333
- import { fileURLToPath as fileURLToPath4 } from "url";
239334
- var __filename3 = fileURLToPath4(import.meta.url);
239335
- var __dirname3 = dirname3(__filename3);
239336
- var CONFIG_DIR3 = join4(homedir4(), ".enact");
239337
- var ENV_BASE_DIR = join4(CONFIG_DIR3, "env");
239338
- function findStaticDir() {
239339
- const candidates = [
239340
- join4(__dirname3, "web", "static"),
239341
- join4(__dirname3, "static"),
239342
- join4(__dirname3, "..", "src", "web", "static"),
239343
- join4(__dirname3, "..", "..", "src", "web", "static"),
239344
- join4(process.cwd(), "src", "web", "static"),
239345
- join4(__dirname3, "..", "..", "..", "src", "web", "static"),
239346
- join4(__dirname3, "..", "..", "src", "web", "static")
239347
- ];
239348
- for (const candidate of candidates) {
239349
- if (existsSync4(join4(candidate, "index.html"))) {
239350
- logger_default.debug(`Found static directory: ${candidate}`);
239351
- return candidate;
239352
- }
239353
- }
239354
- throw new Error("Could not find static directory. Tried: " + candidates.join(", "));
239355
- }
239356
- var STATIC_DIR = findStaticDir();
239357
- function parseDotEnv(content) {
239358
- const vars = {};
239359
- const lines = content.split(`
239360
- `);
239361
- for (const line of lines) {
239362
- const trimmed = line.trim();
239363
- if (!trimmed || trimmed.startsWith("#")) {
239364
- continue;
239365
- }
239366
- const equalIndex = trimmed.indexOf("=");
239367
- if (equalIndex === -1) {
239368
- continue;
239369
- }
239370
- const key = trimmed.slice(0, equalIndex).trim();
239371
- let value = trimmed.slice(equalIndex + 1).trim();
239372
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
239373
- value = value.slice(1, -1);
239374
- }
239375
- if (key) {
239376
- vars[key] = value;
239377
- }
239378
- }
239379
- return vars;
239380
- }
239381
- function generateDotEnv(vars) {
239382
- return Object.entries(vars).map(([key, value]) => {
239383
- const needsQuotes = value.includes(" ") || value.includes("\t") || value.includes(`
239384
- `) || value.includes('"');
239385
- const escapedValue = needsQuotes ? `"${value.replace(/"/g, "\\\"")}"` : value;
239386
- return `${key}=${escapedValue}`;
239387
- }).join(`
239388
- `) + `
239389
- `;
239390
- }
239391
- async function getAllPackageNamespaces() {
239392
- const packages = [];
239393
- if (!existsSync4(ENV_BASE_DIR)) {
239394
- return packages;
239395
- }
239396
- try {
239397
- await scanDirectory(ENV_BASE_DIR, "", packages);
239398
- } catch (error) {
239399
- logger_default.error("Failed to scan env directory:", error);
239400
- }
239401
- return packages;
239402
- }
239403
- async function scanDirectory(dir, relativePath, packages) {
239404
- try {
239405
- const entries = await readdir(dir);
239406
- for (const entry of entries) {
239407
- const fullPath = join4(dir, entry);
239408
- const stats = await stat2(fullPath);
239409
- if (stats.isDirectory()) {
239410
- const newRelativePath = relativePath ? `${relativePath}/${entry}` : entry;
239411
- await scanDirectory(fullPath, newRelativePath, packages);
239412
- } else if (entry === ".env") {
239413
- const namespace = relativePath || "root";
239414
- try {
239415
- const content = await readFile3(fullPath, "utf8");
239416
- const variables = parseDotEnv(content);
239417
- packages.push({
239418
- namespace,
239419
- path: fullPath,
239420
- variables
239421
- });
239422
- } catch (error) {
239423
- logger_default.error(`Failed to read .env file at ${fullPath}:`, error);
239424
- }
239425
- }
239426
- }
239427
- } catch (error) {
239428
- logger_default.error(`Failed to scan directory ${dir}:`, error);
239429
- }
239430
- }
239431
- async function getPackageEnvVars(namespace) {
239432
- const envFile = join4(ENV_BASE_DIR, namespace, ".env");
239433
- if (!existsSync4(envFile)) {
239434
- return {};
239435
- }
239436
- try {
239437
- const content = await readFile3(envFile, "utf8");
239438
- return parseDotEnv(content);
239439
- } catch (error) {
239440
- logger_default.error(`Failed to read env file for ${namespace}:`, error);
239441
- return {};
239442
- }
239443
- }
239444
- async function setPackageEnvVar(namespace, key, value) {
239445
- const envFile = join4(ENV_BASE_DIR, namespace, ".env");
239446
- const envDir = dirname3(envFile);
239447
- if (!existsSync4(envDir)) {
239448
- await mkdir2(envDir, { recursive: true });
239449
- }
239450
- const existingVars = await getPackageEnvVars(namespace);
239451
- existingVars[key] = value;
239452
- const envContent = generateDotEnv(existingVars);
239453
- await writeFile2(envFile, envContent, "utf8");
239454
- }
239455
- async function deletePackageEnvVar(namespace, key) {
239456
- const existingVars = await getPackageEnvVars(namespace);
239457
- if (!(key in existingVars)) {
239458
- throw new Error(`Environment variable '${key}' not found in package '${namespace}'`);
239459
- }
239460
- delete existingVars[key];
239461
- const envFile = join4(ENV_BASE_DIR, namespace, ".env");
239462
- const envContent = generateDotEnv(existingVars);
239463
- await writeFile2(envFile, envContent, "utf8");
239464
- }
239465
- async function serveStaticFile(filePath, res) {
239466
- try {
239467
- const content = await readFile3(filePath, "utf8");
239468
- const ext = filePath.split(".").pop()?.toLowerCase();
239469
- let contentType = "text/plain";
239470
- switch (ext) {
239471
- case "html":
239472
- contentType = "text/html";
239473
- break;
239474
- case "css":
239475
- contentType = "text/css";
239476
- break;
239477
- case "js":
239478
- contentType = "application/javascript";
239479
- break;
239480
- case "json":
239481
- contentType = "application/json";
239482
- break;
239483
- }
239484
- res.writeHead(200, { "Content-Type": contentType });
239485
- res.end(content);
239486
- } catch (error) {
239487
- logger_default.error("Error serving static file:", error);
239488
- res.writeHead(404, { "Content-Type": "text/plain" });
239489
- res.end("File not found");
239490
- }
239491
- }
239492
- async function handleRequest(req, res) {
239493
- const urlParts = parse3(req.url || "", true);
239494
- const pathname = urlParts.pathname || "/";
239495
- const method = req.method || "GET";
239496
- res.setHeader("Access-Control-Allow-Origin", "*");
239497
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
239498
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
239499
- if (method === "OPTIONS") {
239500
- res.writeHead(200);
239501
- res.end();
239502
- return;
239503
- }
239504
- try {
239505
- if (pathname === "/") {
239506
- await serveStaticFile(join4(STATIC_DIR, "index.html"), res);
239507
- } else if (pathname === "/style.css") {
239508
- await serveStaticFile(join4(STATIC_DIR, "style.css"), res);
239509
- } else if (pathname === "/app.js") {
239510
- await serveStaticFile(join4(STATIC_DIR, "app.js"), res);
239511
- } else if (pathname === "/favicon.ico") {
239512
- const favicon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">\uD83C\uDF10</text></svg>`;
239513
- res.writeHead(200, { "Content-Type": "image/svg+xml" });
239514
- res.end(favicon);
239515
- } else if (pathname === "/api/packages" && method === "GET") {
239516
- const packages = await getAllPackageNamespaces();
239517
- res.writeHead(200, { "Content-Type": "application/json" });
239518
- res.end(JSON.stringify({ packages }));
239519
- } else if (pathname === "/api/packages" && method === "POST") {
239520
- const body = await getRequestBody(req);
239521
- const { namespace } = JSON.parse(body);
239522
- if (!namespace) {
239523
- res.writeHead(400, { "Content-Type": "application/json" });
239524
- res.end(JSON.stringify({ error: "Namespace is required" }));
239525
- return;
239526
- }
239527
- const envDir = join4(ENV_BASE_DIR, namespace);
239528
- const envFile = join4(envDir, ".env");
239529
- if (!existsSync4(envDir)) {
239530
- await mkdir2(envDir, { recursive: true });
239531
- }
239532
- if (!existsSync4(envFile)) {
239533
- await writeFile2(envFile, "", "utf8");
239534
- }
239535
- res.writeHead(200, { "Content-Type": "application/json" });
239536
- res.end(JSON.stringify({ success: true }));
239537
- } else if (pathname?.startsWith("/api/packages/") && method === "GET") {
239538
- const namespace = decodeURIComponent(pathname.replace("/api/packages/", ""));
239539
- const variables = await getPackageEnvVars(namespace);
239540
- res.writeHead(200, { "Content-Type": "application/json" });
239541
- res.end(JSON.stringify({ namespace, variables }));
239542
- } else if (pathname?.startsWith("/api/packages/") && pathname.endsWith("/variables") && method === "POST") {
239543
- const namespace = decodeURIComponent(pathname.replace("/api/packages/", "").replace("/variables", ""));
239544
- const body = await getRequestBody(req);
239545
- const { key, value } = JSON.parse(body);
239546
- if (!key || value === undefined) {
239547
- res.writeHead(400, { "Content-Type": "application/json" });
239548
- res.end(JSON.stringify({ error: "Key and value are required" }));
239549
- return;
239550
- }
239551
- await setPackageEnvVar(namespace, key, value);
239552
- res.writeHead(200, { "Content-Type": "application/json" });
239553
- res.end(JSON.stringify({ success: true }));
239554
- } else if (pathname?.includes("/variables/") && method === "DELETE") {
239555
- const pathParts = pathname.split("/");
239556
- const variableIndex = pathParts.indexOf("variables");
239557
- const namespace = decodeURIComponent(pathParts.slice(3, variableIndex).join("/"));
239558
- const key = decodeURIComponent(pathParts[variableIndex + 1]);
239559
- await deletePackageEnvVar(namespace, key);
239560
- res.writeHead(200, { "Content-Type": "application/json" });
239561
- res.end(JSON.stringify({ success: true }));
239562
- } else {
239563
- res.writeHead(404, { "Content-Type": "application/json" });
239564
- res.end(JSON.stringify({ error: "Not found" }));
239565
- }
239566
- } catch (error) {
239567
- logger_default.error("Web server error:", error);
239568
- res.writeHead(500, { "Content-Type": "application/json" });
239569
- res.end(JSON.stringify({
239570
- error: error instanceof Error ? error.message : "Internal server error"
239571
- }));
239572
- }
239573
- }
239574
- function getRequestBody(req) {
239575
- return new Promise((resolve, reject) => {
239576
- let body = "";
239577
- req.on("data", (chunk) => {
239578
- body += chunk.toString();
239579
- });
239580
- req.on("end", () => {
239581
- resolve(body);
239582
- });
239583
- req.on("error", reject);
239584
- });
239585
- }
239586
- function startEnvManagerServer(port = 5555) {
239587
- return new Promise((resolve, reject) => {
239588
- const server2 = createServer(handleRequest);
239589
- server2.listen(port, () => {
239590
- const actualPort = server2.address()?.port || port;
239591
- logger_default.info(`\uD83C\uDF10 Environment Manager web server started on http://localhost:${actualPort}`);
239592
- resolve({ server: server2, port: actualPort });
239593
- });
239594
- server2.on("error", (error) => {
239595
- if (error.code === "EADDRINUSE") {
239596
- server2.listen(0, () => {
239597
- const actualPort = server2.address()?.port;
239598
- logger_default.info(`\uD83C\uDF10 Environment Manager web server started on http://localhost:${actualPort} (port ${port} was in use)`);
239599
- resolve({ server: server2, port: actualPort });
239600
- });
239601
- } else {
239602
- reject(error);
239603
- }
239604
- });
239605
- });
239606
- }
239607
- // ../shared/dist/LocalToolResolver.js
239608
- var yaml2 = __toESM(require_dist3(), 1);
239609
- import { promises as fs5, readFileSync as readFileSync3, writeFileSync } from "fs";
239610
- import { join as join5, resolve, basename as basename2 } from "path";
239611
- class LocalToolResolver {
239612
- constructor(enactCore, localToolsDir = "./tools", cacheDir = "./.tool-cache") {
239613
- this.enactCore = enactCore;
239614
- this.toolCache = new Map;
239615
- this.aliases = new Map;
239616
- this.favorites = new Set;
239617
- this.localToolsDir = resolve(localToolsDir);
239618
- this.cacheDir = resolve(cacheDir);
239619
- this.loadConfiguration();
239620
- }
239621
- async resolveTool(toolName) {
239622
- const resolvedName = this.aliases.get(toolName) || toolName;
239623
- const localTool = await this.getLocalTool(resolvedName);
239624
- if (localTool) {
239625
- return {
239626
- tool: localTool,
239627
- source: "local",
239628
- metadata: { path: localTool.path }
239629
- };
239630
- }
239631
- const cachedTool = this.toolCache.get(resolvedName);
239632
- if (cachedTool && !this.isCacheExpired(cachedTool)) {
239633
- return {
239634
- tool: cachedTool,
239635
- source: "cache",
239636
- metadata: { cachedAt: cachedTool.lastModified }
239637
- };
239638
- }
239639
- try {
239640
- const registryTool = await this.enactCore.getToolByName(resolvedName);
239641
- if (registryTool) {
239642
- await this.cacheRegistryTool(resolvedName, registryTool);
239643
- return {
239644
- tool: registryTool,
239645
- source: "registry",
239646
- metadata: { cached: true }
239647
- };
239648
- }
239649
- } catch (error) {
239650
- logger_default.debug(`Registry lookup failed for ${resolvedName}:`, error);
239651
- }
239652
- return null;
239653
- }
239654
- async getLocalTool(toolName) {
239655
- const possiblePaths = [
239656
- join5(this.localToolsDir, `${toolName}.yaml`),
239657
- join5(this.localToolsDir, `${toolName}.yml`),
239658
- join5(this.localToolsDir, toolName, "tool.yaml"),
239659
- join5(this.localToolsDir, toolName, "tool.yml"),
239660
- join5(this.localToolsDir, toolName, `${toolName}.yaml`),
239661
- join5(this.localToolsDir, toolName, `${toolName}.yml`)
239662
- ];
239663
- for (const toolPath of possiblePaths) {
239664
- try {
239665
- const stats = await fs5.stat(toolPath);
239666
- const content = await fs5.readFile(toolPath, "utf-8");
239667
- const definition = yaml2.parse(content);
239668
- if (definition && (definition.name === toolName || definition.name === undefined)) {
239669
- return {
239670
- name: definition.name || toolName,
239671
- path: toolPath,
239672
- definition,
239673
- lastModified: stats.mtime,
239674
- cached: false
239675
- };
239676
- }
239677
- } catch (error) {
239678
- continue;
239679
- }
239680
- }
239681
- return null;
239682
- }
239683
- async cacheRegistryTool(toolName, tool) {
239684
- try {
239685
- await fs5.mkdir(this.cacheDir, { recursive: true });
239686
- const cachePath = join5(this.cacheDir, `${toolName}.yaml`);
239687
- const cacheData = {
239688
- ...tool,
239689
- _cached: true,
239690
- _cachedAt: new Date().toISOString(),
239691
- _source: "registry"
239692
- };
239693
- await fs5.writeFile(cachePath, yaml2.stringify(cacheData));
239694
- this.toolCache.set(toolName, {
239695
- name: toolName,
239696
- path: cachePath,
239697
- definition: tool,
239698
- lastModified: new Date,
239699
- cached: true
239700
- });
239701
- logger_default.debug(`Cached registry tool: ${toolName}`);
239702
- } catch (error) {
239703
- logger_default.warn(`Failed to cache tool ${toolName}:`, error);
239704
- }
239705
- }
239706
- isCacheExpired(tool, maxAge = 24 * 60 * 60 * 1000) {
239707
- return Date.now() - tool.lastModified.getTime() > maxAge;
239708
- }
239709
- async listAllTools() {
239710
- const localTools = await this.scanLocalTools();
239711
- const cachedTools = Array.from(this.toolCache.values());
239712
- return {
239713
- local: localTools,
239714
- cached: cachedTools,
239715
- favorites: Array.from(this.favorites),
239716
- aliases: Object.fromEntries(this.aliases)
239717
- };
239718
- }
239719
- async scanLocalTools() {
239720
- const tools = [];
239721
- try {
239722
- await fs5.mkdir(this.localToolsDir, { recursive: true });
239723
- const entries = await this.scanDirectory(this.localToolsDir);
239724
- for (const entry of entries) {
239725
- if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
239726
- try {
239727
- const content = await fs5.readFile(entry, "utf-8");
239728
- const definition = yaml2.parse(content);
239729
- if (definition && (definition.name || definition.command)) {
239730
- const stats = await fs5.stat(entry);
239731
- tools.push({
239732
- name: definition.name || basename2(entry, ".yaml").replace(".yml", ""),
239733
- path: entry,
239734
- definition,
239735
- lastModified: stats.mtime,
239736
- cached: false
239737
- });
239738
- }
239739
- } catch (error) {
239740
- logger_default.debug(`Skipping invalid tool file ${entry}:`, error);
239741
- }
239742
- }
239743
- }
239744
- } catch (error) {
239745
- logger_default.warn(`Failed to scan local tools directory:`, error);
239746
- }
239747
- return tools;
239342
+ async scanLocalTools() {
239343
+ const tools = [];
239344
+ try {
239345
+ await fs5.mkdir(this.localToolsDir, { recursive: true });
239346
+ const entries = await this.scanDirectory(this.localToolsDir);
239347
+ for (const entry of entries) {
239348
+ if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
239349
+ try {
239350
+ const content = await fs5.readFile(entry, "utf-8");
239351
+ const definition = yaml2.parse(content);
239352
+ if (definition && (definition.name || definition.command)) {
239353
+ const stats = await fs5.stat(entry);
239354
+ tools.push({
239355
+ name: definition.name || basename2(entry, ".yaml").replace(".yml", ""),
239356
+ path: entry,
239357
+ definition,
239358
+ lastModified: stats.mtime,
239359
+ cached: false
239360
+ });
239361
+ }
239362
+ } catch (error) {
239363
+ logger_default.debug(`Skipping invalid tool file ${entry}:`, error);
239364
+ }
239365
+ }
239366
+ }
239367
+ } catch (error) {
239368
+ logger_default.warn(`Failed to scan local tools directory:`, error);
239369
+ }
239370
+ return tools;
239748
239371
  }
239749
239372
  async scanDirectory(dir) {
239750
239373
  const files = [];
239751
239374
  try {
239752
239375
  const entries = await fs5.readdir(dir, { withFileTypes: true });
239753
239376
  for (const entry of entries) {
239754
- const fullPath = join5(dir, entry.name);
239377
+ const fullPath = join4(dir, entry.name);
239755
239378
  if (entry.isDirectory()) {
239756
239379
  const subFiles = await this.scanDirectory(fullPath);
239757
239380
  files.push(...subFiles);
@@ -239810,13 +239433,13 @@ class LocalToolResolver {
239810
239433
  }
239811
239434
  loadConfiguration() {
239812
239435
  try {
239813
- const configPath = join5(this.localToolsDir, "config.json");
239814
- const config2 = JSON.parse(readFileSync3(configPath, "utf-8"));
239815
- if (config2.aliases) {
239816
- this.aliases = new Map(Object.entries(config2.aliases));
239436
+ const configPath = join4(this.localToolsDir, "config.json");
239437
+ const config = JSON.parse(readFileSync3(configPath, "utf-8"));
239438
+ if (config.aliases) {
239439
+ this.aliases = new Map(Object.entries(config.aliases));
239817
239440
  }
239818
- if (config2.favorites) {
239819
- this.favorites = new Set(config2.favorites);
239441
+ if (config.favorites) {
239442
+ this.favorites = new Set(config.favorites);
239820
239443
  }
239821
239444
  } catch (error) {
239822
239445
  logger_default.debug("No tool configuration found, using defaults");
@@ -239824,13 +239447,13 @@ class LocalToolResolver {
239824
239447
  }
239825
239448
  saveConfiguration() {
239826
239449
  try {
239827
- const configPath = join5(this.localToolsDir, "config.json");
239828
- const config2 = {
239450
+ const configPath = join4(this.localToolsDir, "config.json");
239451
+ const config = {
239829
239452
  aliases: Object.fromEntries(this.aliases),
239830
239453
  favorites: Array.from(this.favorites),
239831
239454
  lastUpdated: new Date().toISOString()
239832
239455
  };
239833
- writeFileSync(configPath, JSON.stringify(config2, null, 2));
239456
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
239834
239457
  } catch (error) {
239835
239458
  logger_default.warn("Failed to save tool configuration:", error);
239836
239459
  }
@@ -239854,11 +239477,11 @@ class LocalToolResolver {
239854
239477
  }
239855
239478
  }
239856
239479
  };
239857
- const samplePath = join5(this.localToolsDir, "hello-world.yaml");
239480
+ const samplePath = join4(this.localToolsDir, "hello-world.yaml");
239858
239481
  await fs5.writeFile(samplePath, yaml2.stringify(sampleTool));
239859
239482
  logger_default.info(`Created sample tool at ${samplePath}`);
239860
239483
  }
239861
- const readmePath = join5(this.localToolsDir, "README.md");
239484
+ const readmePath = join4(this.localToolsDir, "README.md");
239862
239485
  const readme = `# Local Tools Directory
239863
239486
 
239864
239487
  This directory contains your local Enact tools. Tools can be organized as:
@@ -239890,6 +239513,148 @@ Use the MCP tools to manage this directory programmatically.
239890
239513
  }
239891
239514
  }
239892
239515
  var LocalToolResolver_default = LocalToolResolver;
239516
+ // ../shared/dist/utils/help.js
239517
+ var __dirname = "/Users/keithgroves/projects/enact/enact-cli/packages/shared/dist/utils";
239518
+ // ../shared/dist/utils/version.js
239519
+ var __filename = "/Users/keithgroves/projects/enact/enact-cli/packages/shared/dist/utils/version.js";
239520
+ // ../shared/dist/utils/logger.js
239521
+ var LogLevel;
239522
+ (function(LogLevel2) {
239523
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
239524
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
239525
+ LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
239526
+ LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
239527
+ LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
239528
+ })(LogLevel || (LogLevel = {}));
239529
+ var currentLogLevel = LogLevel.INFO;
239530
+ // ../shared/dist/utils/silent-monitor.js
239531
+ class McpSilentOperationMonitor {
239532
+ getOriginalConsoleError() {
239533
+ return this.originalConsoleError;
239534
+ }
239535
+ constructor() {
239536
+ this.isMonitoring = false;
239537
+ this.violations = [];
239538
+ this.consoleOutput = [];
239539
+ this.processExitAttempts = 0;
239540
+ this.readlineUsageDetected = false;
239541
+ this.startTime = 0;
239542
+ this.originalConsoleLog = console.log;
239543
+ this.originalConsoleError = console.error;
239544
+ this.originalConsoleWarn = console.warn;
239545
+ this.originalConsoleInfo = console.info;
239546
+ this.originalProcessExit = process.exit;
239547
+ this.originalStdoutWrite = process.stdout.write;
239548
+ this.originalStderrWrite = process.stderr.write;
239549
+ }
239550
+ startMonitoring() {
239551
+ if (this.isMonitoring) {
239552
+ return;
239553
+ }
239554
+ this.isMonitoring = true;
239555
+ this.violations = [];
239556
+ this.consoleOutput = [];
239557
+ this.processExitAttempts = 0;
239558
+ this.readlineUsageDetected = false;
239559
+ this.startTime = Date.now();
239560
+ console.log = (...args) => {
239561
+ const message = args.join(" ");
239562
+ this.consoleOutput.push(`[LOG] ${message}`);
239563
+ this.violations.push(`Console.log called: ${message}`);
239564
+ };
239565
+ console.error = (...args) => {
239566
+ const message = args.join(" ");
239567
+ this.consoleOutput.push(`[ERROR] ${message}`);
239568
+ this.violations.push(`Console.error called: ${message}`);
239569
+ };
239570
+ console.warn = (...args) => {
239571
+ const message = args.join(" ");
239572
+ this.consoleOutput.push(`[WARN] ${message}`);
239573
+ this.violations.push(`Console.warn called: ${message}`);
239574
+ };
239575
+ console.info = (...args) => {
239576
+ const message = args.join(" ");
239577
+ this.consoleOutput.push(`[INFO] ${message}`);
239578
+ this.violations.push(`Console.info called: ${message}`);
239579
+ };
239580
+ process.exit = (code) => {
239581
+ this.processExitAttempts++;
239582
+ this.violations.push(`Process.exit called with code: ${code}`);
239583
+ throw new Error(`Process exit intercepted: ${code}`);
239584
+ };
239585
+ process.stdout.write = (chunk, ...args) => {
239586
+ if (typeof chunk === "string" && chunk.trim()) {
239587
+ this.consoleOutput.push(`[STDOUT] ${chunk}`);
239588
+ this.violations.push(`Process.stdout.write called: ${chunk.substring(0, 100)}...`);
239589
+ }
239590
+ return true;
239591
+ };
239592
+ process.stderr.write = (chunk, ...args) => {
239593
+ if (typeof chunk === "string" && chunk.trim()) {
239594
+ this.consoleOutput.push(`[STDERR] ${chunk}`);
239595
+ this.violations.push(`Process.stderr.write called: ${chunk.substring(0, 100)}...`);
239596
+ }
239597
+ return true;
239598
+ };
239599
+ const originalRequire = __require;
239600
+ const monitor = this;
239601
+ global.require = function(id) {
239602
+ if (id === "readline" || id.includes("readline")) {
239603
+ monitor.readlineUsageDetected = true;
239604
+ monitor.violations.push("Readline module usage detected");
239605
+ }
239606
+ return originalRequire.apply(this, arguments);
239607
+ };
239608
+ }
239609
+ stopMonitoring() {
239610
+ if (!this.isMonitoring) {
239611
+ throw new Error("Monitor is not currently running");
239612
+ }
239613
+ console.log = this.originalConsoleLog;
239614
+ console.error = this.originalConsoleError;
239615
+ console.warn = this.originalConsoleWarn;
239616
+ console.info = this.originalConsoleInfo;
239617
+ process.exit = this.originalProcessExit;
239618
+ process.stdout.write = this.originalStdoutWrite;
239619
+ process.stderr.write = this.originalStderrWrite;
239620
+ global.require = __require;
239621
+ this.isMonitoring = false;
239622
+ return {
239623
+ violations: [...this.violations],
239624
+ consoleOutputDetected: [...this.consoleOutput],
239625
+ processExitAttempts: this.processExitAttempts,
239626
+ readlineUsageDetected: this.readlineUsageDetected,
239627
+ duration: Date.now() - this.startTime,
239628
+ timestamp: new Date().toISOString()
239629
+ };
239630
+ }
239631
+ isCurrentlyMonitoring() {
239632
+ return this.isMonitoring;
239633
+ }
239634
+ getViolations() {
239635
+ return [...this.violations];
239636
+ }
239637
+ }
239638
+ var globalMonitor = new McpSilentOperationMonitor;
239639
+ function validateSilentEnvironment() {
239640
+ const issues = [];
239641
+ if (process.env.CI !== "true") {
239642
+ issues.push('CI environment variable not set to "true"');
239643
+ }
239644
+ if (process.env.ENACT_SKIP_INTERACTIVE !== "true") {
239645
+ issues.push('ENACT_SKIP_INTERACTIVE not set to "true"');
239646
+ }
239647
+ if (process.env.DEBUG === "true" || process.env.VERBOSE === "true") {
239648
+ issues.push("DEBUG or VERBOSE environment variables are enabled");
239649
+ }
239650
+ if (process.stdin.isTTY) {
239651
+ issues.push("Process is running in TTY mode (potentially interactive)");
239652
+ }
239653
+ return {
239654
+ valid: issues.length === 0,
239655
+ issues
239656
+ };
239657
+ }
239893
239658
  // ../shared/dist/services/McpCoreService.js
239894
239659
  class McpCoreService {
239895
239660
  constructor(options) {
@@ -239969,6 +239734,287 @@ class McpCoreService {
239969
239734
  }
239970
239735
  }
239971
239736
  var mcpCoreService = new McpCoreService;
239737
+ // ../shared/dist/web/env-manager-server.js
239738
+ import { createServer } from "http";
239739
+ import { parse as parse4 } from "url";
239740
+ import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, readdir, stat as stat2 } from "fs/promises";
239741
+ import { existsSync as existsSync4 } from "fs";
239742
+ import { join as join5, dirname as dirname3 } from "path";
239743
+ import { homedir as homedir4 } from "os";
239744
+ import { fileURLToPath as fileURLToPath4 } from "url";
239745
+ var __filename3 = fileURLToPath4(import.meta.url);
239746
+ var __dirname3 = dirname3(__filename3);
239747
+ var CONFIG_DIR3 = join5(homedir4(), ".enact");
239748
+ var ENV_BASE_DIR = join5(CONFIG_DIR3, "env");
239749
+ function findStaticDir() {
239750
+ const candidates = [
239751
+ join5(__dirname3, "web", "static"),
239752
+ join5(__dirname3, "static"),
239753
+ join5(__dirname3, "..", "src", "web", "static"),
239754
+ join5(__dirname3, "..", "..", "src", "web", "static"),
239755
+ join5(process.cwd(), "src", "web", "static"),
239756
+ join5(__dirname3, "..", "..", "..", "src", "web", "static"),
239757
+ join5(__dirname3, "..", "..", "src", "web", "static")
239758
+ ];
239759
+ for (const candidate of candidates) {
239760
+ if (existsSync4(join5(candidate, "index.html"))) {
239761
+ logger_default.debug(`Found static directory: ${candidate}`);
239762
+ return candidate;
239763
+ }
239764
+ }
239765
+ throw new Error("Could not find static directory. Tried: " + candidates.join(", "));
239766
+ }
239767
+ var STATIC_DIR = findStaticDir();
239768
+ function parseDotEnv(content) {
239769
+ const vars = {};
239770
+ const lines = content.split(`
239771
+ `);
239772
+ for (const line of lines) {
239773
+ const trimmed = line.trim();
239774
+ if (!trimmed || trimmed.startsWith("#")) {
239775
+ continue;
239776
+ }
239777
+ const equalIndex = trimmed.indexOf("=");
239778
+ if (equalIndex === -1) {
239779
+ continue;
239780
+ }
239781
+ const key = trimmed.slice(0, equalIndex).trim();
239782
+ let value = trimmed.slice(equalIndex + 1).trim();
239783
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
239784
+ value = value.slice(1, -1);
239785
+ }
239786
+ if (key) {
239787
+ vars[key] = value;
239788
+ }
239789
+ }
239790
+ return vars;
239791
+ }
239792
+ function generateDotEnv(vars) {
239793
+ return Object.entries(vars).map(([key, value]) => {
239794
+ const needsQuotes = value.includes(" ") || value.includes("\t") || value.includes(`
239795
+ `) || value.includes('"');
239796
+ const escapedValue = needsQuotes ? `"${value.replace(/"/g, "\\\"")}"` : value;
239797
+ return `${key}=${escapedValue}`;
239798
+ }).join(`
239799
+ `) + `
239800
+ `;
239801
+ }
239802
+ async function getAllPackageNamespaces() {
239803
+ const packages = [];
239804
+ if (!existsSync4(ENV_BASE_DIR)) {
239805
+ return packages;
239806
+ }
239807
+ try {
239808
+ await scanDirectory(ENV_BASE_DIR, "", packages);
239809
+ } catch (error) {
239810
+ logger_default.error("Failed to scan env directory:", error);
239811
+ }
239812
+ return packages;
239813
+ }
239814
+ async function scanDirectory(dir, relativePath, packages) {
239815
+ try {
239816
+ const entries = await readdir(dir);
239817
+ for (const entry of entries) {
239818
+ const fullPath = join5(dir, entry);
239819
+ const stats = await stat2(fullPath);
239820
+ if (stats.isDirectory()) {
239821
+ const newRelativePath = relativePath ? `${relativePath}/${entry}` : entry;
239822
+ await scanDirectory(fullPath, newRelativePath, packages);
239823
+ } else if (entry === ".env") {
239824
+ const namespace = relativePath || "root";
239825
+ try {
239826
+ const content = await readFile3(fullPath, "utf8");
239827
+ const variables = parseDotEnv(content);
239828
+ packages.push({
239829
+ namespace,
239830
+ path: fullPath,
239831
+ variables
239832
+ });
239833
+ } catch (error) {
239834
+ logger_default.error(`Failed to read .env file at ${fullPath}:`, error);
239835
+ }
239836
+ }
239837
+ }
239838
+ } catch (error) {
239839
+ logger_default.error(`Failed to scan directory ${dir}:`, error);
239840
+ }
239841
+ }
239842
+ async function getPackageEnvVars(namespace) {
239843
+ const envFile = join5(ENV_BASE_DIR, namespace, ".env");
239844
+ if (!existsSync4(envFile)) {
239845
+ return {};
239846
+ }
239847
+ try {
239848
+ const content = await readFile3(envFile, "utf8");
239849
+ return parseDotEnv(content);
239850
+ } catch (error) {
239851
+ logger_default.error(`Failed to read env file for ${namespace}:`, error);
239852
+ return {};
239853
+ }
239854
+ }
239855
+ async function setPackageEnvVar(namespace, key, value) {
239856
+ const envFile = join5(ENV_BASE_DIR, namespace, ".env");
239857
+ const envDir = dirname3(envFile);
239858
+ if (!existsSync4(envDir)) {
239859
+ await mkdir2(envDir, { recursive: true });
239860
+ }
239861
+ const existingVars = await getPackageEnvVars(namespace);
239862
+ existingVars[key] = value;
239863
+ const envContent = generateDotEnv(existingVars);
239864
+ await writeFile2(envFile, envContent, "utf8");
239865
+ }
239866
+ async function deletePackageEnvVar(namespace, key) {
239867
+ const existingVars = await getPackageEnvVars(namespace);
239868
+ if (!(key in existingVars)) {
239869
+ throw new Error(`Environment variable '${key}' not found in package '${namespace}'`);
239870
+ }
239871
+ delete existingVars[key];
239872
+ const envFile = join5(ENV_BASE_DIR, namespace, ".env");
239873
+ const envContent = generateDotEnv(existingVars);
239874
+ await writeFile2(envFile, envContent, "utf8");
239875
+ }
239876
+ async function serveStaticFile(filePath, res) {
239877
+ try {
239878
+ const content = await readFile3(filePath, "utf8");
239879
+ const ext = filePath.split(".").pop()?.toLowerCase();
239880
+ let contentType = "text/plain";
239881
+ switch (ext) {
239882
+ case "html":
239883
+ contentType = "text/html";
239884
+ break;
239885
+ case "css":
239886
+ contentType = "text/css";
239887
+ break;
239888
+ case "js":
239889
+ contentType = "application/javascript";
239890
+ break;
239891
+ case "json":
239892
+ contentType = "application/json";
239893
+ break;
239894
+ }
239895
+ res.writeHead(200, { "Content-Type": contentType });
239896
+ res.end(content);
239897
+ } catch (error) {
239898
+ logger_default.error("Error serving static file:", error);
239899
+ res.writeHead(404, { "Content-Type": "text/plain" });
239900
+ res.end("File not found");
239901
+ }
239902
+ }
239903
+ async function handleRequest(req, res) {
239904
+ const urlParts = parse4(req.url || "", true);
239905
+ const pathname = urlParts.pathname || "/";
239906
+ const method = req.method || "GET";
239907
+ res.setHeader("Access-Control-Allow-Origin", "*");
239908
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
239909
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
239910
+ if (method === "OPTIONS") {
239911
+ res.writeHead(200);
239912
+ res.end();
239913
+ return;
239914
+ }
239915
+ try {
239916
+ if (pathname === "/") {
239917
+ await serveStaticFile(join5(STATIC_DIR, "index.html"), res);
239918
+ } else if (pathname === "/style.css") {
239919
+ await serveStaticFile(join5(STATIC_DIR, "style.css"), res);
239920
+ } else if (pathname === "/app.js") {
239921
+ await serveStaticFile(join5(STATIC_DIR, "app.js"), res);
239922
+ } else if (pathname === "/favicon.ico") {
239923
+ const favicon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">\uD83C\uDF10</text></svg>`;
239924
+ res.writeHead(200, { "Content-Type": "image/svg+xml" });
239925
+ res.end(favicon);
239926
+ } else if (pathname === "/api/packages" && method === "GET") {
239927
+ const packages = await getAllPackageNamespaces();
239928
+ res.writeHead(200, { "Content-Type": "application/json" });
239929
+ res.end(JSON.stringify({ packages }));
239930
+ } else if (pathname === "/api/packages" && method === "POST") {
239931
+ const body = await getRequestBody(req);
239932
+ const { namespace } = JSON.parse(body);
239933
+ if (!namespace) {
239934
+ res.writeHead(400, { "Content-Type": "application/json" });
239935
+ res.end(JSON.stringify({ error: "Namespace is required" }));
239936
+ return;
239937
+ }
239938
+ const envDir = join5(ENV_BASE_DIR, namespace);
239939
+ const envFile = join5(envDir, ".env");
239940
+ if (!existsSync4(envDir)) {
239941
+ await mkdir2(envDir, { recursive: true });
239942
+ }
239943
+ if (!existsSync4(envFile)) {
239944
+ await writeFile2(envFile, "", "utf8");
239945
+ }
239946
+ res.writeHead(200, { "Content-Type": "application/json" });
239947
+ res.end(JSON.stringify({ success: true }));
239948
+ } else if (pathname?.startsWith("/api/packages/") && method === "GET") {
239949
+ const namespace = decodeURIComponent(pathname.replace("/api/packages/", ""));
239950
+ const variables = await getPackageEnvVars(namespace);
239951
+ res.writeHead(200, { "Content-Type": "application/json" });
239952
+ res.end(JSON.stringify({ namespace, variables }));
239953
+ } else if (pathname?.startsWith("/api/packages/") && pathname.endsWith("/variables") && method === "POST") {
239954
+ const namespace = decodeURIComponent(pathname.replace("/api/packages/", "").replace("/variables", ""));
239955
+ const body = await getRequestBody(req);
239956
+ const { key, value } = JSON.parse(body);
239957
+ if (!key || value === undefined) {
239958
+ res.writeHead(400, { "Content-Type": "application/json" });
239959
+ res.end(JSON.stringify({ error: "Key and value are required" }));
239960
+ return;
239961
+ }
239962
+ await setPackageEnvVar(namespace, key, value);
239963
+ res.writeHead(200, { "Content-Type": "application/json" });
239964
+ res.end(JSON.stringify({ success: true }));
239965
+ } else if (pathname?.includes("/variables/") && method === "DELETE") {
239966
+ const pathParts = pathname.split("/");
239967
+ const variableIndex = pathParts.indexOf("variables");
239968
+ const namespace = decodeURIComponent(pathParts.slice(3, variableIndex).join("/"));
239969
+ const key = decodeURIComponent(pathParts[variableIndex + 1]);
239970
+ await deletePackageEnvVar(namespace, key);
239971
+ res.writeHead(200, { "Content-Type": "application/json" });
239972
+ res.end(JSON.stringify({ success: true }));
239973
+ } else {
239974
+ res.writeHead(404, { "Content-Type": "application/json" });
239975
+ res.end(JSON.stringify({ error: "Not found" }));
239976
+ }
239977
+ } catch (error) {
239978
+ logger_default.error("Web server error:", error);
239979
+ res.writeHead(500, { "Content-Type": "application/json" });
239980
+ res.end(JSON.stringify({
239981
+ error: error instanceof Error ? error.message : "Internal server error"
239982
+ }));
239983
+ }
239984
+ }
239985
+ function getRequestBody(req) {
239986
+ return new Promise((resolve2, reject) => {
239987
+ let body = "";
239988
+ req.on("data", (chunk) => {
239989
+ body += chunk.toString();
239990
+ });
239991
+ req.on("end", () => {
239992
+ resolve2(body);
239993
+ });
239994
+ req.on("error", reject);
239995
+ });
239996
+ }
239997
+ function startEnvManagerServer(port = 5555) {
239998
+ return new Promise((resolve2, reject) => {
239999
+ const server2 = createServer(handleRequest);
240000
+ server2.listen(port, () => {
240001
+ const actualPort = server2.address()?.port || port;
240002
+ logger_default.info(`\uD83C\uDF10 Environment Manager web server started on http://localhost:${actualPort}`);
240003
+ resolve2({ server: server2, port: actualPort });
240004
+ });
240005
+ server2.on("error", (error) => {
240006
+ if (error.code === "EADDRINUSE") {
240007
+ server2.listen(0, () => {
240008
+ const actualPort = server2.address()?.port;
240009
+ logger_default.info(`\uD83C\uDF10 Environment Manager web server started on http://localhost:${actualPort} (port ${port} was in use)`);
240010
+ resolve2({ server: server2, port: actualPort });
240011
+ });
240012
+ } else {
240013
+ reject(error);
240014
+ }
240015
+ });
240016
+ });
240017
+ }
239972
240018
  // ../shared/dist/lib/enact-direct.js
239973
240019
  class EnactDirect {
239974
240020
  constructor(options = {}) {
@@ -240141,7 +240187,8 @@ server2.registerTool("execute-tool-by-name", {
240141
240187
  dryRun: exports_external.boolean().optional().describe("Dry run mode"),
240142
240188
  verbose: exports_external.boolean().optional().describe("Verbose output"),
240143
240189
  async: exports_external.boolean().optional().describe("Run in background for long operations"),
240144
- forceRegistry: exports_external.boolean().optional().describe("Skip local resolution and go straight to registry")
240190
+ forceRegistry: exports_external.boolean().optional().describe("Skip local resolution and go straight to registry"),
240191
+ mount: exports_external.string().optional().describe("Mount local directory to container (format: 'localPath' or 'localPath:containerPath')")
240145
240192
  }
240146
240193
  }, async (params) => {
240147
240194
  const {
@@ -240153,7 +240200,8 @@ server2.registerTool("execute-tool-by-name", {
240153
240200
  dryRun,
240154
240201
  verbose,
240155
240202
  async = false,
240156
- forceRegistry = false
240203
+ forceRegistry = false,
240204
+ mount
240157
240205
  } = params;
240158
240206
  try {
240159
240207
  logger_default.info(`Executing tool: ${name} (localFile: ${localFile}, async: ${async})`);
@@ -240244,7 +240292,7 @@ ${suggestions.map((s2) => ` • ${s2}`).join(`
240244
240292
  }
240245
240293
  const isLongRunning = toolToExecute.name.includes("dagger") || toolToExecute.name.includes("docker") || toolToExecute.name.includes("build") || async;
240246
240294
  if (isLongRunning) {
240247
- const operationId = `${toolToExecute.name}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
240295
+ const operationId = `${toolToExecute.name}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
240248
240296
  let executionPromise;
240249
240297
  if (isLocalFile) {
240250
240298
  const yamlContent = await import("fs/promises").then((fs6) => fs6.readFile(toolToExecute.path, "utf-8"));
@@ -240253,6 +240301,7 @@ ${suggestions.map((s2) => ` • ${s2}`).join(`
240253
240301
  force: dangerouslySkipVerification || true,
240254
240302
  dryRun,
240255
240303
  verbose,
240304
+ mount,
240256
240305
  isLocalFile: true
240257
240306
  });
240258
240307
  } else {
@@ -240261,6 +240310,7 @@ ${suggestions.map((s2) => ` • ${s2}`).join(`
240261
240310
  force: dangerouslySkipVerification,
240262
240311
  dryRun,
240263
240312
  verbose,
240313
+ mount,
240264
240314
  isLocalFile: false
240265
240315
  });
240266
240316
  }
@@ -240303,14 +240353,16 @@ Operation ID: ${operationId}
240303
240353
  timeout: timeout3 || "120s",
240304
240354
  force: dangerouslySkipVerification || true,
240305
240355
  dryRun,
240306
- verbose
240356
+ verbose,
240357
+ mount
240307
240358
  });
240308
240359
  } else {
240309
240360
  result = await enactCore.executeToolByName(toolToExecute.name || name, inputs, {
240310
240361
  timeout: timeout3 || "120s",
240311
240362
  force: dangerouslySkipVerification,
240312
240363
  dryRun,
240313
- verbose
240364
+ verbose,
240365
+ mount
240314
240366
  });
240315
240367
  }
240316
240368
  if (!result.success) {