Metadata-Version: 2.4
Name: loomeye-agent
Version: 0.2.1
Summary: LoomEye HPC Agent — connect your cluster to LoomEye
License: MIT
Project-URL: Homepage, https://loomeye.com
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: websockets>=14.0

# LoomEye HPC Agent

The LoomEye HPC Agent is a lightweight Python command-line tool that runs on a
user's remote machine, workstation, or HPC cluster. It opens an outbound
WebSocket connection to LoomEye so the web app can browse approved files and,
when the user opts in, run generated Python code on that machine.

The agent does not require inbound ports, SSH access from LoomEye, or exposing
the full filesystem. It runs as the current OS user and can only access paths
that user can already access.

## Quick Start

Install the current LoomEye agent release:

```bash
python3 -m pip install --user loomeye-agent
python3 -m loomeye_agent --version
```

On Windows, replace `python3` with `py`.

Get an agent token from the LoomEye web app:

```text
Sidebar -> HPC Files -> Connect Cluster
```

Then connect:

```bash
python3 -m loomeye_agent connect --token <YOUR_TOKEN>
```

The agent asks which directory LoomEye may access. Press Enter to expose the
current directory, or enter one or more paths:

```text
Directory to expose [/home/warren/project]:
```

You can also skip the prompt:

```bash
python3 loomeye_agent.py connect \
  --token <YOUR_TOKEN> \
  --dirs /home/warren/project /scratch/my-job
```

Keep the process running while you use LoomEye. For long sessions, run it inside
`tmux` or `screen`.

## Commands

| Command | Purpose |
| --- | --- |
| `python3 loomeye_agent.py -h` | Show top-level help. |
| `python3 loomeye_agent.py connect --token <TOKEN>` | Connect to LoomEye prod. Prompts for directories if needed. |
| `python3 loomeye_agent.py connect --dev --token <TOKEN>` | Connect to the LoomEye dev backend. |
| `python3 loomeye_agent.py connect --config ~/.loomeye/agent.json` | Reuse saved connection settings. |
| `python3 loomeye_agent.py status` | Print saved config, with the token masked. |
| `python3 loomeye_agent.py doctor --dirs <DIR> --file <PATH>` | Check local sandbox and selected-file wiring. |
| `python3 loomeye_agent.py disconnect` | Delete the saved config file. |

## Important Options

| Option | Applies To | Meaning |
| --- | --- | --- |
| `--token <TOKEN>` | `connect` | One-time token generated by the LoomEye web app. |
| `--dirs <PATHS...>` | `connect` | Directories LoomEye may browse/read. Optional in interactive terminals. |
| `--dev` | `connect` | Use the dev backend instead of prod. |
| `--env-dir <PATH>` | `connect` | Use a different managed Python environment directory. |
| `--selected-file <PATH>` | `doctor` | Validate a file as if it were selected in the LoomEye UI. Repeat for multiple files. |
| `--run-python` | `doctor` | Also run a tiny script that checks the generated-code prelude. |

`--allow-exec` and `--allow-install` still exist for automation/backward
compatibility, but they are hidden from help. Normal users choose those settings
through interactive prompts.

## Prod And Dev

Prod is the default:

```bash
python3 loomeye_agent.py connect --token <TOKEN>
```

Dev is explicit:

```bash
python3 -m loomeye_agent connect --dev --token <TOKEN>
```

The backend URLs are built into the agent so users do not need to copy or edit
them:

```text
prod: wss://math-solver-prod-696616516071.us-west1.run.app
dev:  wss://math-solver-dev-696616516071.us-west1.run.app
```

## Directory Access

LoomEye only sees files inside the approved sandbox roots.

On first connect, if `--dirs` is omitted, the agent prompts for directories. The
prompt supports:

- pressing Enter to use the current directory
- one directory path
- multiple space-separated directory paths
- quoted paths that contain spaces

Examples:

```text
Directory to expose [/home/warren/project]: /home/warren/data
Directory to expose [/home/warren/project]: /data/job1 /scratch/job1
Directory to expose [/home/warren/project]: "/home/warren/my project"
```

The sandbox resolves each path to its real absolute path. Any request outside
those roots is rejected.

## Local Wiring Check

Use `doctor` when you want to confirm that a local HPC file will be passed to
LoomEye analysis correctly before doing a live demo:

```bash
python3 loomeye_agent.py doctor \
  --dirs /home/warren/project \
  --file /home/warren/project/data.csv
```

That validates the sandbox roots, normalizes the selected file paths, and prints
the metadata LoomEye will expose to generated code. To also verify the execution
prelude used for remote Python runs:

```bash
python3 loomeye_agent.py doctor \
  --dirs /home/warren/project \
  --file /home/warren/project/data.csv \
  --run-python
```

## Saved Config

After a successful connection, the agent saves settings to:

```text
~/.loomeye/agent.json
```

The config includes:

- persistent agent token
- allowed directories
- backend URL
- Python execution permission
- package install permission
- managed env path
- whether execution choices should be remembered

The file is written with `0600` permissions because it contains a sensitive
token. The token can reconnect from the same machine until it is revoked or
replaced. To remove it from this machine:

```bash
python3 loomeye_agent.py disconnect
```

To inspect it without printing the full token:

```bash
python3 loomeye_agent.py status
```

## Python Execution

By default, the agent only exposes approved files. During `connect`, it asks:

```text
Allow LoomEye to run generated Python code on this machine?
```

If you answer yes, LoomEye can send generated Python code to the connected
machine. Code runs in the managed LoomEye Python environment, not in the user's
shell environment.

The agent also asks:

```text
Allow installing missing Python packages into ~/.loomeye/python-env?
```

If enabled, LoomEye may request package installation when generated code imports
a package that is not available.

## Managed Python Environment

Default environment:

```text
~/.loomeye/python-env
```

The agent creates this environment on demand with Python's built-in `venv`
module:

- `with_pip=True`
- `system_site_packages=True`

That means the environment has its own package install location, but it can also
see system/user site packages when available.

Override the location:

```bash
python3 loomeye_agent.py connect \
  --token <TOKEN> \
  --env-dir /scratch/warren/loomeye-python-env
```

## Environment Scanning

When Python execution is enabled, the agent scans the managed environment on
connect. It reports:

- Python executable path
- Python version
- installed package names
- package count
- importable top-level module count
- env directory
- scan timestamp

The scan result is cached in memory. The backend can ask for the cached summary
or force a refresh. After auto-installing packages, the agent rescans.

## Missing Package Detection

Before running code, the agent parses imports using Python `ast`. It checks
whether each import is available with `importlib.util.find_spec`.

If imports are missing and auto-install is disabled, execution stops and LoomEye
returns the missing package list.

If auto-install is enabled, the agent maps common import names to pip package
names and runs:

```bash
python -m pip install <packages>
```

Examples of mappings:

| Import | Package |
| --- | --- |
| `PIL` | `pillow` |
| `cv2` | `opencv-python` |
| `sklearn` | `scikit-learn` |
| `bs4` | `beautifulsoup4` |
| `yaml` | `PyYAML` |
| `docx` | `python-docx` |
| `pptx` | `python-pptx` |

## Remote Commands Supported By The Agent

These are internal WebSocket commands sent by the LoomEye backend:

| Command | Purpose |
| --- | --- |
| `list_files` | List files inside allowed directories. |
| `read_file` | Read text files or return binary files as base64. |
| `file_info` | Return metadata for a file or directory. |
| `search_files` | Search filenames inside allowed directories. |
| `head_file` | Read the first N lines of a text file. |
| `python_env` | Return the cached or refreshed Python environment summary. |
| `check_imports` | Parse code imports and report missing packages. |
| `run_python` | Run Python code in the managed environment. |

Safety limits include:

- max file read: 50 MB
- max directory listing: 5,000 entries
- default execution timeout: 240 seconds
- max execution timeout: 900 seconds
- max execution output capture: 2 MB

## How Code Runs

For `run_python`, the agent:

1. validates that Python execution is enabled
2. validates the working directory is inside an allowed sandbox root
3. checks imports
4. optionally installs missing packages
5. writes the generated code to `~/.loomeye/runs/<run_id>/code.py`
6. runs that script with the managed Python executable
7. returns stdout, stderr, return code, installed packages, working directory,
   and Python executable path

The script receives two helper values through `builtins`:

```python
builtins.loomeye_hpc_files
builtins.loomeye_hpc_file_metadata
builtins.loomeye_working_dir
```

The same values are also available as top-level names in the generated script:
`loomeye_hpc_files`, `loomeye_hpc_file_metadata`, and
`loomeye_working_dir`.

## Publishing To PyPI

The package metadata lives in `agent/pyproject.toml`, and releases are published
from `.github/workflows/publish-loomeye-agent.yml`.

Release flow:

```bash
cd agent
python -m build
twine check dist/*
cd ..
git tag loomeye-agent-v0.2.0
git push origin loomeye-agent-v0.2.0
```

The GitHub Actions workflow publishes the built package from `agent/dist` using
PyPI Trusted Publishing. Each release needs a new version in
`agent/pyproject.toml` before tagging.

After the first PyPI release, users can install with:

```bash
python3 -m pip install --user --upgrade loomeye-agent==0.2.0
python3 -m loomeye_agent connect --token <TOKEN>
```

## Troubleshooting

### Agent connects to the wrong backend

Prod is default. Use `--dev` for dev:

```bash
python3 loomeye_agent.py connect --dev --token <TOKEN>
```

There is also a hidden internal override for emergency testing:

```bash
python3 loomeye_agent.py connect \
  --backend-url wss://example.com \
  --token <TOKEN>
```

### Directory does not show up in LoomEye

Make sure the directory exists on the remote machine and was included in the
prompt or `--dirs`. Run:

```bash
python3 loomeye_agent.py status
```

### Python packages are missing

Reconnect and allow package installs, or install packages manually into:

```text
~/.loomeye/python-env
```

Manual install example:

```bash
~/.loomeye/python-env/bin/python -m pip install pandas numpy scipy
```

## Security Model

- The agent initiates an outbound WebSocket connection.
- LoomEye does not need inbound network access to the user's machine.
- LoomEye does not receive SSH keys.
- File access is limited to approved sandbox directories.
- Python execution is opt-in.
- Package installation is a separate opt-in.
- The agent runs as the current user and inherits that user's OS permissions.
- The saved token is masked in `status` output and stored in a `0600` config file.
