or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-interface.mdcore-execution.mdenvironment-loading.mdfile-formats.mdindex.mdutilities.md
tile.json

core-execution.mddocs/

Core Environment Command Execution

The core execution system provides the main functionality for spawning commands with environment variables loaded from files. It handles process management, signal propagation, and environment variable processing.

Capabilities

EnvCmd Function

Main function for executing commands with environment variables loaded from various file sources.

/**
 * The main env-cmd program. This will spawn a new process and run the given command using
 * various environment file solutions.
 * 
 * @param options - Configuration options for command execution
 * @returns Promise resolving to the environment variables used
 */
function EnvCmd(
  {
    command,
    commandArgs,
    envFile,
    rc,
    options = {},
  }: EnvCmdOptions,
): Promise<Environment>;

interface EnvCmdOptions extends GetEnvVarOptions {
  /** Command to execute */
  command: string;
  /** Arguments to pass to the command */
  commandArgs: string[];
  /** Options controlling execution behavior */
  options?: {
    /** Expand $var and ${var} in command and arguments */
    expandEnvs?: boolean;
    /** Recursively expand environment variables */
    recursive?: boolean;
    /** Don't override existing process environment variables */
    noOverride?: boolean;
    /** Silent mode - suppress error output */
    silent?: boolean;
    /** Use shell for command execution */
    useShell?: boolean;
    /** Verbose logging output */
    verbose?: boolean;
  };
}

Usage Examples:

import { EnvCmd } from "env-cmd";

// Basic command execution with .env file
const env = await EnvCmd({
  command: "node",
  commandArgs: ["server.js"],
  envFile: { filePath: ".env" }
});

// Execute with variable expansion
const env = await EnvCmd({
  command: "echo",
  commandArgs: ["API URL: $API_URL"],
  envFile: { filePath: ".env.production" },
  options: {
    expandEnvs: true,
    verbose: true
  }
});

// Use RC file with multiple environments
const env = await EnvCmd({
  command: "npm",
  commandArgs: ["test"],
  rc: {
    environments: ["test", "staging"],
    filePath: ".env-cmdrc"
  },
  options: {
    recursive: true,
    useShell: true
  }
});

Signal Handling System

Process signal management for proper termination handling between parent and child processes.

/**
 * Signal handling class for managing process termination
 */
class TermSignals {
  /** Internal flag to track if exit has been called */
  _exitCalled: boolean;
  
  /**
   * Create a new signal handler instance
   * @param options - Configuration options
   */
  constructor(options?: { verbose?: boolean });
  
  /**
   * Handle termination signals for the given child process
   * Sets up bidirectional signal handling between parent and child processes
   * @param proc - Child process to manage signals for
   */
  handleTermSignals(proc: ChildProcess): void;
  
  /**
   * Handle uncaught exceptions in the parent process
   * Enables catching of unhandled exceptions with proper cleanup
   */
  handleUncaughtExceptions(): void;
  
  /**
   * Terminate parent process helper with signal or exit code
   * @param signal - Signal name or exit code to use for termination
   */
  _terminateProcess(signal?: NodeJS.Signals | number): void;
  
  /**
   * Clean up all registered process event listeners
   */
  _removeProcessListeners(): void;
  
  /**
   * General exception handler for uncaught exceptions
   * @param e - Error object from uncaught exception
   */
  _uncaughtExceptionHandler(e: Error): void;
}

Usage Examples:

import { TermSignals } from "env-cmd/dist/signal-termination.js";
import { spawn } from "child_process";

// Create signal handler with verbose logging
const signals = new TermSignals({ verbose: true });

// Spawn a child process
const proc = spawn("node", ["server.js"], {
  stdio: 'inherit',
  env: process.env
});

// Set up signal handling for proper cleanup
signals.handleTermSignals(proc);
signals.handleUncaughtExceptions();

Handled Signals:

The TermSignals class handles the following POSIX signals:

  • SIGINT - Interrupt signal (Ctrl+C)
  • SIGTERM - Termination request
  • SIGHUP - Hangup signal

Signal Flow:

  1. Parent to Child: When parent receives termination signal, it kills the child process and then terminates itself
  2. Child to Parent: When child process exits, parent process is also terminated with the same exit code/signal
  3. Cleanup: All event listeners are properly removed to prevent memory leaks
  4. Exit Prevention: _exitCalled flag prevents signal/exit loops

Environment Variable Processing

The EnvCmd function performs several processing steps on environment variables:

  1. Loading: Environment variables are loaded from the specified file source
  2. Merging: Variables are merged with system environment based on override settings
  3. Expansion: Variable expansion is applied if recursive option is enabled
  4. Command Processing: Command and arguments are expanded if expandEnvs is enabled

Processing Order:

// 1. Load from file source
let env = await getEnvVars({ envFile, rc, verbose });

// 2. Merge with system environment
if (options.noOverride === true) {
  env = Object.assign({}, env, process.env);
} else {
  env = Object.assign({}, process.env, env);
}

// 3. Recursive expansion if enabled
if (options.recursive === true) {
  for (const key of Object.keys(env)) {
    if (env[key] !== undefined) {
      env[key] = expandEnvs(env[key], env);
    }
  }
}

// 4. Command expansion if enabled
if (options.expandEnvs === true) {
  command = expandEnvs(command, env);
  commandArgs = commandArgs.map(arg => expandEnvs(arg, env));
}

Error Handling

The EnvCmd function handles various error conditions:

  • File Loading Errors: Propagated from getEnvVars unless silent mode is enabled
  • Command Execution Errors: Child process spawn failures are handled by cross-spawn
  • Signal Handling: Proper cleanup and termination of child processes
  • Invalid Commands: Throws error if command is empty or undefined

Silent Mode Behavior:

try {
  env = await getEnvVars({ envFile, rc, verbose: options.verbose });
} catch (e) {
  if (!(options.silent ?? false)) {
    throw e;
  }
  // Continue with empty environment in silent mode
}

Process Spawning

Commands are executed using the cross-spawn library for cross-platform compatibility:

const proc = spawn(command, commandArgs, {
  stdio: 'inherit',
  shell: options.useShell,
  env,
});

Features:

  • Cross-platform: Works on Windows, macOS, and Linux
  • Shell support: Optional shell execution via useShell option
  • Stdio inheritance: Child process output is displayed in parent terminal
  • Environment injection: Merged environment variables are passed to child process