@enactprotocol/mcp-server 1.2.4 → 1.2.6

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