diff --git a/libs/agent/agent/providers/openai/loop.py b/libs/agent/agent/providers/openai/loop.py index c4e0dfb5..baff6ff0 100644 --- a/libs/agent/agent/providers/openai/loop.py +++ b/libs/agent/agent/providers/openai/loop.py @@ -201,16 +201,7 @@ class OpenAILoop(BaseLoop): # Emit screenshot callbacks await self.handle_screenshot(screenshot_base64, action_type="initial_state") - - # Save screenshot if requested - if self.save_trajectory: - # Ensure screenshot_base64 is a string - if not isinstance(screenshot_base64, str): - logger.warning( - "Converting non-string screenshot_base64 to string for _save_screenshot" - ) - self._save_screenshot(screenshot_base64, action_type="state") - logger.info("Screenshot saved to trajectory") + self._save_screenshot(screenshot_base64, action_type="state") # First add any existing user messages that were passed to run() user_query = None @@ -351,6 +342,7 @@ class OpenAILoop(BaseLoop): # Process screenshot through hooks action_type = f"after_{action.get('type', 'action')}" await self.handle_screenshot(screenshot_base64, action_type=action_type) + self._save_screenshot(screenshot_base64, action_type=action_type) # Create computer_call_output computer_call_output = { @@ -397,6 +389,7 @@ class OpenAILoop(BaseLoop): # Process the response # await self.response_handler.process_response(response, queue) + self._log_api_call("agent_response", request=None, response=response) await queue.put(response) except Exception as e: logger.error(f"Error executing computer action: {str(e)}") diff --git a/libs/computer/computer/interface/macos.py b/libs/computer/computer/interface/macos.py index 2460086c..dcac6342 100644 --- a/libs/computer/computer/interface/macos.py +++ b/libs/computer/computer/interface/macos.py @@ -335,7 +335,15 @@ class MacOSComputerInterface(BaseComputerInterface): # Keyboard Actions async def type_text(self, text: str) -> None: - await self._send_command("type_text", {"text": text}) + # Temporary fix for https://github.com/trycua/cua/issues/165 + # Check if text contains Unicode characters + if any(ord(char) > 127 for char in text): + # For Unicode text, use clipboard and paste + await self.set_clipboard(text) + await self.hotkey(Key.COMMAND, 'v') + else: + # For ASCII text, use the regular typing method + await self._send_command("type_text", {"text": text}) async def press(self, key: "KeyType") -> None: """Press a single key. @@ -523,7 +531,7 @@ class MacOSComputerInterface(BaseComputerInterface): result = await self._send_command("get_accessibility_tree") if not result.get("success", False): raise RuntimeError(result.get("error", "Failed to get accessibility tree")) - return result.get("tree", {}) + return result async def get_active_window_bounds(self) -> Dict[str, int]: """Get the bounds of the currently active window.""" diff --git a/libs/lume/scripts/install.sh b/libs/lume/scripts/install.sh index 171076e7..11629f49 100755 --- a/libs/lume/scripts/install.sh +++ b/libs/lume/scripts/install.sh @@ -12,6 +12,16 @@ GREEN=$(tput setaf 2) BLUE=$(tput setaf 4) YELLOW=$(tput setaf 3) +# Check if running as root or with sudo +if [ "$(id -u)" -eq 0 ] || [ -n "$SUDO_USER" ]; then + echo "${RED}Error: Do not run this script with sudo or as root.${NORMAL}" + echo "If you need to install to a system directory, create it first with proper permissions:" + echo " sudo mkdir -p /desired/directory && sudo chown $(whoami) /desired/directory" + echo "Then run the installer normally:" + echo " ./install.sh --install-dir=/desired/directory" + exit 1 +fi + # Default installation directory (user-specific, doesn't require sudo) DEFAULT_INSTALL_DIR="$HOME/.local/bin" INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" diff --git a/libs/lume/src/FileSystem/Home.swift b/libs/lume/src/FileSystem/Home.swift index d83b39b0..634e3bef 100644 --- a/libs/lume/src/FileSystem/Home.swift +++ b/libs/lume/src/FileSystem/Home.swift @@ -72,6 +72,22 @@ final class Home { /// - Returns: A VMDirectory instance /// - Throws: HomeError if location not found func getVMDirectory(_ name: String, storage: String? = nil) throws -> VMDirectory { + // Special case for ephemeral storage using macOS temporary directory + if let storage = storage, storage == "ephemeral" { + // Get the current temporary directory + let tmpDir = ProcessInfo.processInfo.environment["TMPDIR"] ?? "/tmp" + // Remove trailing slash if present + let cleanPath = tmpDir.hasSuffix("/") ? String(tmpDir.dropLast()) : tmpDir + + // Create the directory if it doesn't exist + if !fileExists(at: cleanPath) { + try createVMLocation(at: cleanPath) + } + + let baseDir = Path(cleanPath) + return VMDirectory(baseDir.directory(name)) + } + let location: VMLocation if let storage = storage { @@ -123,6 +139,55 @@ final class Home { // Loop through all locations let settings = settingsManager.getSettings() + + // Also check ephemeral directory (macOS temporary directory) + let tmpDir = ProcessInfo.processInfo.environment["TMPDIR"] ?? "/tmp" + let cleanPath = tmpDir.hasSuffix("/") ? String(tmpDir.dropLast()) : tmpDir + + // If tmp directory exists, check for VMs there + if fileExists(at: cleanPath) { + let tmpDirPath = Path(cleanPath) + do { + let directoryURL = URL(fileURLWithPath: cleanPath) + let contents = try FileManager.default.contentsOfDirectory( + at: directoryURL, + includingPropertiesForKeys: [.isDirectoryKey], + options: .skipsHiddenFiles + ) + + for subdir in contents { + do { + guard let isDirectory = try subdir.resourceValues(forKeys: [.isDirectoryKey]).isDirectory, + isDirectory else { + continue + } + + let vmName = subdir.lastPathComponent + let vmDir = VMDirectory(tmpDirPath.directory(vmName)) + + // Only include if it's a valid VM directory + if vmDir.initialized() { + results.append(VMDirectoryWithLocation( + directory: vmDir, + locationName: "ephemeral" + )) + } + } catch { + // Skip any directories we can't access + continue + } + } + } catch { + Logger.error( + "Failed to access ephemeral directory", + metadata: [ + "path": cleanPath, + "error": error.localizedDescription, + ] + ) + // Continue to regular locations rather than failing completely + } + } for location in settings.vmLocations { let locationPath = Path(location.expandedPath) diff --git a/libs/lumier/.dockerignore b/libs/lumier/.dockerignore index 3e0f9c98..4a4b321a 100644 --- a/libs/lumier/.dockerignore +++ b/libs/lumier/.dockerignore @@ -22,3 +22,4 @@ venv/ test-results/ # Ignore anything else you don't want in the Docker build context +./examples \ No newline at end of file diff --git a/libs/lumier/Dockerfile b/libs/lumier/Dockerfile index 06829adf..c66bb9f7 100644 --- a/libs/lumier/Dockerfile +++ b/libs/lumier/Dockerfile @@ -30,10 +30,10 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* # Download and install noVNC without caching -RUN wget https://github.com/trycua/noVNC/archive/refs/heads/master.zip -O master.zip && \ - unzip master.zip && \ +RUN wget https://github.com/trycua/noVNC/archive/refs/heads/master.zip -O master1.zip && \ + unzip master1.zip && \ mv noVNC-master /opt/noVNC && \ - rm master.zip + rm master1.zip # Set environment variables for noVNC ENV NOVNC_PATH="/opt/noVNC" diff --git a/libs/lumier/README.md b/libs/lumier/README.md index ffdb4c5f..826d5097 100644 --- a/libs/lumier/README.md +++ b/libs/lumier/README.md @@ -55,10 +55,7 @@ Here's what's happening behind the scenes: # 1. Navigate to the Lumier directory cd libs/lumier -# 2. Build the Docker image (first-time setup) -docker build -t lumier:latest . - -# 3. Run the container with temporary storage +# 2. Run the container with temporary storage (using pre-built image from Docker Hub) docker run -it --rm \ --name lumier-vm \ -p 8006:8006 \ @@ -66,7 +63,7 @@ docker run -it --rm \ -e VERSION=ghcr.io/trycua/macos-sequoia-cua:latest \ -e CPU_CORES=4 \ -e RAM_SIZE=8192 \ - lumier:latest + trycua/lumier:latest ``` After running the command above, you can access your macOS VM through a web browser (e.g., http://localhost:8006). @@ -91,7 +88,7 @@ docker run -it --rm \ -e CPU_CORES=4 \ -e RAM_SIZE=8192 \ -e HOST_STORAGE_PATH=$(pwd)/storage \ - lumier:latest + trycua/lumier:latest ``` This command creates a connection between a folder on your Mac (`$(pwd)/storage`) and a folder inside the Docker container (`/storage`). The `-v` flag (volume mount) and the `HOST_STORAGE_PATH` variable work together to ensure your VM data is saved on your host Mac. @@ -116,11 +113,131 @@ docker run -it --rm \ -e RAM_SIZE=8192 \ -e HOST_STORAGE_PATH=$(pwd)/storage \ -e HOST_SHARED_PATH=$(pwd)/shared \ - lumier:latest + trycua/lumier:latest ``` With this setup, any files you place in the `shared` folder on your Mac will be accessible from within the macOS VM, and vice versa. +## Automating VM Startup with on-logon.sh + +You can automatically run scripts when the VM starts up by placing an `on-logon.sh` script in the shared folder's lifecycle directory. This is useful for setting up your VM environment each time it starts. + +```bash +# Create the lifecycle directory in your shared folder +mkdir -p shared/lifecycle + +# Create a sample on-logon.sh script +cat > shared/lifecycle/on-logon.sh << 'EOF' +#!/usr/bin/env bash + +# Create a file on the desktop +echo "Hello from Lumier!" > /Users/lume/Desktop/hello_lume.txt + +# You can add more commands to execute at VM startup +# For example: +# - Configure environment variables +# - Start applications +# - Mount network drives +# - Set up development environments +EOF + +# Make the script executable +chmod +x shared/lifecycle/on-logon.sh +``` + +The script will be automatically executed when the VM starts up. It runs in the VM context and has access to: + +- The `/Users/lume` user directory (home directory in the VM) +- The shared folder at `/Volumes/My Shared Files` inside the VM +- Any resources available to the VM + +This feature enables automation of VM setup without modifying the base VM image. + +## Using Docker Compose + +You can also use Docker Compose to run Lumier with a simple configuration file. Create a `docker-compose.yml` file with the following content: + +```yaml +version: '3' + +services: + lumier: + image: trycua/lumier:latest + container_name: lumier-vm + restart: unless-stopped + ports: + - "8006:8006" # Port for VNC access + volumes: + - ./storage:/storage # VM persistent storage + - ./shared:/shared # Shared folder accessible in the VM + environment: + - VM_NAME=lumier-vm + - VERSION=ghcr.io/trycua/macos-sequoia-cua:latest + - CPU_CORES=4 + - RAM_SIZE=8192 + - HOST_STORAGE_PATH=${PWD}/storage + - HOST_SHARED_PATH=${PWD}/shared + stop_signal: SIGINT + stop_grace_period: 2m +``` + +Then run Lumier using: + +```bash +# First create the required directories +mkdir -p storage shared + +# Start the container +docker-compose up -d + +# View the logs +docker-compose logs -f + +# Stop the container when done +docker-compose down +``` + +## Building and Customizing Lumier + +If you want to customize the Lumier container or build it from source, you can follow these steps: + +```bash +# 1. Navigate to the Lumier directory +cd libs/lumier + +# 2. Build the Docker image locally +docker build -t lumier-custom:latest . + +# 3. Run your custom build +docker run -it --rm \ + --name lumier-vm \ + -p 8006:8006 \ + -e VM_NAME=lumier-vm \ + -e VERSION=ghcr.io/trycua/macos-sequoia-cua:latest \ + -e CPU_CORES=4 \ + -e RAM_SIZE=8192 \ + lumier-custom:latest +``` + +### Customization Options + +The Dockerfile provides several customization points: + +1. **Base image**: The container uses Debian Bullseye Slim as the base. You can modify this if needed. +2. **Installed packages**: You can add or remove packages in the apt-get install list. +3. **Hooks**: Check the `/run/hooks/` directory for scripts that run at specific points during VM lifecycle. +4. **Configuration**: Review `/run/config/constants.sh` for default settings. + +After making your modifications, you can build and push your custom image to your own Docker Hub repository: + +```bash +# Build with a custom tag +docker build -t yourusername/lumier:custom . + +# Push to Docker Hub (after docker login) +docker push yourusername/lumier:custom +``` + ## Configuration Options When running Lumier, you'll need to configure a few things: diff --git a/libs/lumier/examples/Custom.Dockerfile b/libs/lumier/examples/Custom.Dockerfile deleted file mode 100644 index 9938f656..00000000 --- a/libs/lumier/examples/Custom.Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -# Custom Lumier image that uses the base lumier:latest image -# and overrides environment variables as needed -FROM trycua/lumier:latest - -# Default environment variables that can be overridden at build time -# These values will override the defaults from the base image -ARG CUSTOM_VERSION="ghcr.io/trycua/macos-sequoia-vanilla:latest" -ARG CUSTOM_RAM_SIZE="16384" -ARG CUSTOM_CPU_CORES="8" -ARG CUSTOM_DISK_SIZE="100" -ARG CUSTOM_DISPLAY="1024x768" -ARG CUSTOM_VM_NAME="custom-vanilla-lumier" - -# Set environment variables based on build args -ENV VERSION=${CUSTOM_VERSION} -ENV RAM_SIZE=${CUSTOM_RAM_SIZE} -ENV CPU_CORES=${CUSTOM_CPU_CORES} -ENV DISK_SIZE=${CUSTOM_DISK_SIZE} -ENV DISPLAY=${CUSTOM_DISPLAY} -ENV VM_NAME=${CUSTOM_VM_NAME} - -# Create the necessary directory for lifecycle scripts -RUN mkdir -p /run/lifecycle - -# Copy custom on-logon script to be executed inside the VM after login -COPY src/lifecycle/on-logon.sh /run/lifecycle/on-logon.sh - -# Make sure the script is executable -RUN chmod +x /run/lifecycle/on-logon.sh - -# We're using the default entrypoint from the base image diff --git a/libs/lumier/examples/docker-compose.yml b/libs/lumier/examples/docker-compose.yml deleted file mode 100644 index 96afbd18..00000000 --- a/libs/lumier/examples/docker-compose.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: '3' - -# IMPORTANT: Before using this docker-compose file, start the tunnel manually: -# socat TCP-LISTEN:8080,reuseaddr,fork EXEC:"$(pwd)/../src/bin/tunnel.sh" & - -services: - lumier: - image: lumier:latest # or trycua/lumier:latest if using the Docker Hub image - container_name: lumier-vm - restart: unless-stopped - ports: - - "8006:8006" # Port for VNC access - volumes: - - ../storage:/storage # VM persistent storage (relative path from docker-compose.yml) - - ../shared:/data # Shared folder accessible in the VM - environment: - - VM_NAME=lumier-vm - - VERSION=ghcr.io/trycua/macos-sequoia-vanilla:latest # Default MacOS image - - CPU_CORES=4 # Number of CPU cores - - RAM_SIZE=8192 # RAM in MB - - HOST_STORAGE_PATH=${PWD}/../storage # Required for Docker-only setup - - HOST_SHARED_PATH=${PWD}/../shared # Required for Docker-only setup - - LUMIER_DEBUG=0 # Set to 1 for debug mode - # Network mode host is optional but can improve performance - # network_mode: host - - # Uncomment the following lines if needed for KVM virtualization - # devices: - # - /dev/kvm - # - /dev/net/tun - stop_signal: SIGINT - stop_grace_period: 2m - -# Note: When using Docker Compose for Lumier, you're responsible for: -# - Starting and managing the tunnel (using socat as shown above) -# - Building the Docker image before first use (docker-compose build) -# - Providing the correct environment variables (HOST_STORAGE_PATH and HOST_SHARED_PATH) -# -# To stop the tunnel when done: -# 1. Find the process: lsof -i TCP:8080 -# 2. Kill it by PID: kill -# -# Access the VM via VNC at: http://localhost:8006/vnc.html -# The password will be displayed in the logs (docker-compose logs) diff --git a/libs/lumier/src/bin/entry.sh b/libs/lumier/src/bin/entry.sh index 44d37b9d..c9cd2f0f 100755 --- a/libs/lumier/src/bin/entry.sh +++ b/libs/lumier/src/bin/entry.sh @@ -31,14 +31,12 @@ if [ -z "${VM_NAME:-}" ]; then export VM_NAME fi -# Set HOST_STORAGE_PATH to a macOS ephemeral path if not set +# Set HOST_STORAGE_PATH to a lume ephemeral storage if not set if [ -z "${HOST_STORAGE_PATH:-}" ]; then - # Use macOS /private/tmp directory which gets automatically cleaned - # This is the proper temporary directory on macOS that's regularly purged - HOST_STORAGE_PATH="/private/tmp/lumier_storage" + HOST_STORAGE_PATH="ephemeral" # Tell user that ephemeral storage is being used - echo "Using ephemeral storage at ${HOST_STORAGE_PATH}. VM state will be lost when macOS cleans up temporary files." + echo "Using ephemeral storage. VM state will be lost when macOS cleans up temporary files." export HOST_STORAGE_PATH fi @@ -56,6 +54,12 @@ if [ "${LUMIER_DEBUG:-0}" == "1" ]; then # fi fi +# Check if we're running as PID 1 (important for Docker signal handling) +if [ $$ -ne 1 ]; then + echo "Warning: This script is not running as PID 1 (current PID: $$)." + echo "Docker signal handling may not work properly when stopped from Docker Desktop." +fi + # Log startup info echo "Lumier VM is starting..." @@ -73,43 +77,21 @@ cleanup() { fi # Attempt to clean up ephemeral storage if it's in the /private/tmp directory - if [[ "$HOST_STORAGE_PATH" == "/private/tmp/lumier_"* ]]; then - echo "[cleanup] Checking if VM exists before cleanup..." - + if [[ "$HOST_STORAGE_PATH" == "ephemeral" ]]; then # First check if VM actually exists VM_INFO=$(lume_get "$VM_NAME" "$HOST_STORAGE_PATH" "json" "false") # Only try VM deletion if VM exists and not in the middle of a pull if [[ "$PULL_IN_PROGRESS" != "1" && $VM_INFO != *"Virtual machine not found"* ]]; then - echo "[cleanup] Removing VM and storage using API: $HOST_STORAGE_PATH" + echo "[cleanup] Cleaning up VM..." lume_delete "$VM_NAME" "$HOST_STORAGE_PATH" > /dev/null 2>&1 - else - echo "[cleanup] No VM found or pull was interrupted, skipping API deletion" fi fi - # Now gently stop noVNC proxy if running - # if [ -n "${NOVNC_PID:-}" ] && kill -0 "$NOVNC_PID" 2>/dev/null; then - # echo "[cleanup] Stopping noVNC proxy (PID $NOVNC_PID)..." - # kill -TERM "$NOVNC_PID" - # # Wait up to 5s for noVNC to exit - # for i in {1..5}; do - # if ! kill -0 "$NOVNC_PID" 2>/dev/null; then - # echo "[cleanup] noVNC proxy stopped." - # break - # fi - # sleep 1 - # done - # # Escalate if still running - # if kill -0 "$NOVNC_PID" 2>/dev/null; then - # echo "[cleanup] noVNC proxy did not exit, killing..." - # kill -KILL "$NOVNC_PID" 2>/dev/null - # fi - # fi - echo "[cleanup] Done. Exiting." exit 0 } -trap cleanup SIGTERM SIGINT +# Ensure we catch all typical container termination signals +trap cleanup SIGTERM SIGINT SIGHUP # Now enable strict error handling after initialization set -euo pipefail @@ -130,7 +112,7 @@ if [ -n "${VNC_PORT:-}" ] && [ -n "${VNC_PASSWORD:-}" ]; then ${NOVNC_PATH}/utils/novnc_proxy --vnc host.docker.internal:${VNC_PORT} --listen 8006 --web ${NOVNC_PATH} > /dev/null 2>&1 & NOVNC_PID=$! disown $NOVNC_PID - echo "noVNC interface available at: http://localhost:PORT/vnc.html?password=${VNC_PASSWORD}&autoconnect=true (replace PORT with the port you forwarded to 8006)" + echo "noVNC interface available at: http://localhost:8006/vnc.html?password=${VNC_PASSWORD}&autoconnect=true (replace PORT with the port you forwarded to 8006)" fi echo "Lumier is running. Press Ctrl+C to stop." diff --git a/libs/lumier/src/hooks/on-logon.sh b/libs/lumier/src/hooks/on-logon.sh index 39f4b41d..94cdbcca 100755 --- a/libs/lumier/src/hooks/on-logon.sh +++ b/libs/lumier/src/hooks/on-logon.sh @@ -5,53 +5,56 @@ # $2: HOST_SHARED_PATH (Path inside VM where host shared dir is mounted, e.g., /Volumes/My Shared Files) VNC_PASSWORD="$1" -HOST_SHARED_PATH="$2" - -# Define the path to the user's optional on-logon script within the shared folder -USER_ON_LOGON_SCRIPT_PATH="$HOST_SHARED_PATH/lifecycle/on-logon.sh" +# IMPORTANT: In the VM, the shared folder is always mounted at this fixed location +HOST_SHARED_PATH="/Volumes/My Shared Files" # Set default value for VNC_DEBUG if not provided VNC_DEBUG=${VNC_DEBUG:-0} -# Only show debug logs if VNC_DEBUG is enabled +# Define the path to the user's optional on-logon script within the shared folder +USER_ON_LOGON_SCRIPT_PATH="$HOST_SHARED_PATH/lifecycle/on-logon.sh" + +# Show basic information when debug is enabled if [ "$VNC_DEBUG" = "1" ]; then - echo "[Remote] Lumier entry point script starting..." - echo "[Remote] Checking for user script at: $USER_ON_LOGON_SCRIPT_PATH" + echo "[VM] Lumier lifecycle script starting" + echo "[VM] Looking for user script: $USER_ON_LOGON_SCRIPT_PATH" fi # Check if the user-provided script exists if [ -f "$USER_ON_LOGON_SCRIPT_PATH" ]; then - # Only show debug logs if VNC_DEBUG is enabled if [ "$VNC_DEBUG" = "1" ]; then - echo "[Remote] Found user script. Making executable and running..." + echo "[VM] Found user script: $USER_ON_LOGON_SCRIPT_PATH" fi + # Always show what script we're executing + echo "[VM] Executing user lifecycle script" + + # Make script executable chmod +x "$USER_ON_LOGON_SCRIPT_PATH" - - # Execute the user script in a subshell, passing VNC password and shared path as arguments - "$USER_ON_LOGON_SCRIPT_PATH" "$VNC_PASSWORD" "$HOST_SHARED_PATH" - - # Capture exit code (optional, but good practice) + + # Execute the user script in a subshell with error output captured + "$USER_ON_LOGON_SCRIPT_PATH" "$VNC_PASSWORD" "$HOST_SHARED_PATH" 2>&1 + + # Capture exit code USER_SCRIPT_EXIT_CODE=$? - # Only show debug logs if VNC_DEBUG is enabled - if [ "$VNC_DEBUG" = "1" ]; then - echo "[Remote] User script finished with exit code: $USER_SCRIPT_EXIT_CODE." + # Always report script execution results + if [ $USER_SCRIPT_EXIT_CODE -eq 0 ]; then + echo "[VM] User lifecycle script completed successfully" + else + echo "[VM] User lifecycle script failed with exit code: $USER_SCRIPT_EXIT_CODE" + fi + + # Check results (only in debug mode) + if [ "$VNC_DEBUG" = "1" ]; then + # List any files created by the script + echo "[VM] Files created by user script:" + ls -la /Users/lume/Desktop/hello_*.txt 2>/dev/null || echo "[VM] No script-created files found" fi - - # Propagate the exit code if non-zero (optional) - # if [ $USER_SCRIPT_EXIT_CODE -ne 0 ]; then - # exit $USER_SCRIPT_EXIT_CODE - # fi else - # Only show debug logs if VNC_DEBUG is enabled if [ "$VNC_DEBUG" = "1" ]; then - echo "[Remote] No user-provided on-logon script found at $USER_ON_LOGON_SCRIPT_PATH. Skipping." + echo "[VM] No user lifecycle script found" fi fi -# Only show debug logs if VNC_DEBUG is enabled -if [ "$VNC_DEBUG" = "1" ]; then - echo "[Remote] Lumier entry point script finished." -fi -exit 0 # Ensure the entry point script exits cleanly if no user script or user script succeeded +exit 0 # Ensure the entry point script exits cleanly diff --git a/libs/lumier/src/lib/utils.sh b/libs/lumier/src/lib/utils.sh index 2c61acb8..8a993754 100755 --- a/libs/lumier/src/lib/utils.sh +++ b/libs/lumier/src/lib/utils.sh @@ -62,9 +62,11 @@ execute_remote_script() { echo "VNC password exported to VM: $vnc_password" fi - # Set a default mount point for data in the VM if data_folder is provided + # Set the shared folder path for the VM if [ -n "$data_folder" ]; then + # VM always sees shared folders at this path, regardless of container path shared_folder_path="/Volumes/My Shared Files" + # Only show path in debug mode if [ "${LUMIER_DEBUG:-0}" == "1" ]; then echo "Data folder path in VM: $shared_folder_path" @@ -104,10 +106,18 @@ execute_remote_script() { fi # Use a here-document to send the script content - # Add -q for completely silent operation, redirect stderr to /dev/null - sshpass -p "$password" ssh -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR "$user@$host" "bash -s -- '$vnc_password' '$data_folder'" 2>/dev/null <&1 </dev/null <