Metadata-Version: 2.4
Name: techlens-agent
Version: 1.0.1
Summary: This tool safely and securely analyzes applications for benchmarking.
Author-email: Crosslake Technologies <techlens@crosslaketech.com>
License-File: LICENSE
Classifier: License :: Free for non-commercial use
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.12
Requires-Dist: azure-identity==1.25.1
Requires-Dist: azure-mgmt-compute==38.0.0
Requires-Dist: azure-mgmt-monitor==7.0.0
Requires-Dist: azure-mgmt-network==30.2.0
Requires-Dist: azure-mgmt-resource==25.0.0
Requires-Dist: azure-mgmt-storage==25.0.0
Requires-Dist: azure-monitor-query==1.4.0
Requires-Dist: boto3==1.43.6
Requires-Dist: chardet==5.2.0
Requires-Dist: defusedxml==0.7.1
Requires-Dist: flask==3.1.3
Requires-Dist: gemfileparser==0.8.0
Requires-Dist: gitpython==3.1.50
Requires-Dist: google-cloud-compute==1.48.0
Requires-Dist: google-cloud-monitoring==2.31.0
Requires-Dist: google-cloud-storage==3.11.0
Requires-Dist: gunicorn==26.0.0
Requires-Dist: httpx[http2]==0.28.1
Requires-Dist: idna==3.15
Requires-Dist: jinja2==3.1.6
Requires-Dist: johnnydep==2.1.0
Requires-Dist: networkx==3.6.1
Requires-Dist: ollama==0.6.2
Requires-Dist: psutil==7.2.0
Requires-Dist: pydantic==2.13.4
Requires-Dist: pygments==2.20.0
Requires-Dist: pygount==3.2.0
Requires-Dist: pyjwt==2.13.0
Requires-Dist: python-multipart==0.0.27
Requires-Dist: pyyaml==6.0.2
Requires-Dist: scipy==1.17.1
Requires-Dist: semgrep==1.166.0
Requires-Dist: setuptools==82.0.1
Requires-Dist: techlens-cache-utils==0.3.1
Requires-Dist: techlens-pygments-tsx==0.5.0
Requires-Dist: urllib3==2.7.0
Requires-Dist: windows-curses==2.4.0; sys_platform == 'win32'
Provides-Extra: dev
Requires-Dist: black>=24.1.1; extra == 'dev'
Requires-Dist: coverage; extra == 'dev'
Requires-Dist: jsonschema; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest-xdist; extra == 'dev'
Description-Content-Type: text/markdown

[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
# TechLens™ Agent

 TechLens Agent™ is one of many tools Crosslake uses to guide your critical technology decisions with data-driven confidence. To learn more, drop us a note at: techlens@crosslaketech.com.

## Requirements:
 - Python3 - Test with `python3 --version`
 - pip - Test with `pip -V`
 - SSH access to code repositories - Test with `git status`
 - Command line tool access to cloud hosting providers
 - Admin privileges on the computer used to run the agent (not required but recommended)
 - Outbound internet access (for posting results and fetching dependency metadata)
 - Your dependency management tools (e.g. `npm` or `yarn` or `maven`)

## To run the Agent:
 - Download the `techlens-agent` binary from crosslake
 - Run the agent `techlens-agent --config=/path/to/config`

## Config Options
 - If you want to check the output for yourself you can set `should_upload: false`, and use the flag `--output=/path/to/dir`. This will give you the chance to inspect what we collect before uploading. For large repositories, it is a lot of information, but we never upload your code or any credentials, just the summary data we collect.

## Troubleshooting:
### Python
 - Run `python3 -m pip install --upgrade pip setuptools wheel`
### git
 - Run `which git`, `git --version`
 - Run ` ssh -vT git@github.com` to test access to GitHub
###  AWS
 - Run `which aws`, `aws --version`
### Azure
 - Run `az git`, `az --version`
 - Run `az account subscription list` to check subscription Id
### Semgrep
 - Run `which semgrep`, `semgrep --version`
### pip
 - Run `which pip`
 - If no `pip`, run:
    `curl -o get-pip.py https://bootstrap.pypa.io/get-pip.py`
    `python get-pip.py`  OR `python3 get-pip.py`
 Run `sudo apt update`

 Copyright ©2023-2025 Crosslake Technologies.


## Server Schema Verification

Verify that agent output (dependencies + stats) conforms to the server's API schema. Uses the server's tsoa-generated
`swagger.json` as the single source of truth.

### Quick check against a local server checkout

```bash
./scripts/verify_server_schema.sh              # defaults to ../techlens
./scripts/verify_server_schema.sh /path/to/techlens
```

### Check against a remote GitHub branch

```bash
./scripts/verify_server_schema.sh --remote          # uat branch
./scripts/verify_server_schema.sh --remote release   # release branch
```

### Directly via pytest

```bash
pytest tests/test_schema_compat.py --server-local ../techlens
pytest tests/test_schema_compat.py --server-branch release
```

### Updating vendored fixtures

When the server schema changes, re-sync the vendored fixtures so CI runs against the latest contract:

```bash
python scripts/sync_server_schema.py --local ../techlens   # from local checkout
python scripts/sync_server_schema.py --branch release       # from GitHub
```

This writes `tests/fixtures/server_swagger.json` and `tests/fixtures/server_stats_schema.json`.

## Run with Docker

The container's entrypoint accepts agent flags directly, so the recommended
invocation is flags-only — no need to spell out the script path:

```bash
docker run -it --rm \
    -v "$PWD/config.yaml:/scan/config.yaml" \
    -v "$PWD/results:/home/techlens/results" \
    techlens-agent -d
```

- `/scan/config.yaml` is auto-detected by the agent, so mounting your config
  there avoids passing `-c`. An explicit `-c` still takes precedence.
- `/home/techlens/results` is where the agent writes output; bind it to a
  host directory to see results after the run.

> **Non-interactive by default.** The agent does not prompt for confirmation,
> so it runs unattended from Docker Desktop, `docker compose up`, or CI — there
> is no "proceed with the scan?" prompt to block on. Pass `--confirm` (alias
> `--interactive`) if you want an interactive gate before the scan starts. If
> both are given, `-y/--yes` takes precedence and skips the prompt.

Other forms still work for backward compatibility:

- `docker run techlens-agent` — no args, runs the default agent / wizard flow.
- `docker run techlens-agent python /techlens/agent.py -d` — explicit full
  command, for advanced overrides.

## Run with Docker Compose
You can build and run the Techlens Agent container with Docker Compose. This mirrors the included run-docker.sh script but makes it easy to pass arguments and reuse mounts.

### Build the image
- docker compose -f docker-compose.yml build techlens-agent

## Build environments

The agent depends on the private `corsis-tech/code-analysis` package, pinned in
`internal-deps.txt`. Which ref each environment's image is built against:

| Environment | Trigger | code-analysis ref |
| --- | --- | --- |
| **integration** | push to `main` (`integration.yaml`) | `main` |
| uat | push to `release` (`uat.yaml`) | `release` |
| production | `workflow_dispatch` (`production.yaml`) | `release` |
| local / PR CI / tests | `docker compose build`, `build_docker.yml`, `_test.yaml` | `release` |

Integration tracks `code-analysis@main` so QA can verify a code-analysis fix
shortly after it merges, without waiting for it to be promoted to the `release`
branch. Every other environment uses the `@release` pin committed in
`internal-deps.txt`.

This is wired through the `CODE_ANALYSIS_REF` Docker build arg (default
`release`). `integration.yaml` passes `code_analysis_ref: main` plus
`no_cache: true` — the `--no-cache` build is required because `main` is a moving
ref, and BuildKit would otherwise serve the `pip install` layer stale and never
re-fetch new commits.

### Refreshing integration with a newer code-analysis main

The integration image rebuilds on every push to this repo's `main`. A merge to
`code-analysis` **main** does not by itself trigger a rebuild, so a fresh fix
otherwise lands in integration only on the next agent `main` push. To pull it
sooner without a code change, re-run the most recent **Integration / Deploy** run
from the **Actions** tab (**Re-run all jobs**): the build is `--no-cache`, so it
re-clones `code-analysis@main` at current `HEAD` and re-uploads the image.

Because the image is tagged with the agent commit (`github.sha`), a re-run
overwrites that tag's artifact in place with newer `code-analysis` content — the
agent commit is unchanged, only the bundled `code-analysis` advances. Use the
`pip freeze` check below to confirm which commit a given image actually carries.

### Which code-analysis commit is an image running?

The image tag identifies the **techlens-agent** commit (the `github.sha` for
integration/uat, the `vX.Y.Z` tag for production). To read the **code-analysis**
commit baked into a given image, query the installed package — `pip freeze`
prints the exact commit it resolved the ref to (PEP 610), even for a moving
branch:

```bash
docker run --rm --entrypoint sh techlens-agent:<tag> \
    -c 'pip freeze | grep -i codeanalysis'
# codeanalysis @ git+ssh://git@github.com/corsis-tech/code-analysis.git@<commit-sha>
```

For an already-running container, `docker exec <container> sh -c 'pip freeze |
grep -i codeanalysis'` does the same. The resolved commit is also visible in the
GitHub Actions build log for the deploy, where the `pip install` step clones
`code-analysis.git` at the ref.

## SSH access for private repositories

To clone private repos over SSH, the container mounts your `~/.ssh` directory
(`${HOME}/.ssh` in `docker-compose.yml`, `$HOME/.ssh` in `run-docker.sh`). On
startup the entrypoint:

- **Auto-discovers your keys.** Any private key in the mounted `~/.ssh` is used,
  even with a non-default name (e.g. `~/.ssh/Github` or `~/.ssh/work_key`). You
  do **not** need to author an `~/.ssh/config` mapping `github.com` to your key.
  If you already have a config with an `IdentityFile`, it is left untouched.
- **Fixes key permissions.** Keys copied in from a Windows/`/mnt` filesystem
  often arrive world-readable; ssh refuses those. The entrypoint tightens them
  to `600` so they are usable.
- **Warns early when no key is found.** If `~/.ssh` is mounted but contains no
  private key, you get a clear message at startup instead of a generic
  `Permission denied (publickey)` later during `git clone`. (Local-only and
  HTTPS scans still run — the warning is informational.)

> The container has no SSH agent. If you mount many keys, ssh tries each in turn;
> GitHub closes the connection after 6 attempts, so keep the mounted `~/.ssh` to
> the keys you actually need. Passphrase-protected keys can't be unlocked
> non-interactively in the container — prefer a passphrase-free key for the scan.

### Running on WSL

If you run the agent from WSL but your SSH key lives on the **Windows** side
(`C:\Users\<you>\.ssh`), the WSL-home `~/.ssh` mount may have no key. The agent
can also mount the Windows-side `~/.ssh` (at `/techlens/.win_ssh`) and **tries
those keys too** (alongside any in the WSL-home `~/.ssh`), so a key stored on
the Windows side works even when your WSL `~/.ssh` is empty.

This mount is **opt-in**: its source must be a real path, because Docker Desktop
refuses to start a container whose bind-mount source does not exist. That is why
it is never wired into `docker-compose.yml` unconditionally — a non-existent
`/mnt/c/...` source would block the very first `docker compose run` on macOS and
native Linux.

- **Installed via the wizard:** the installer detects WSL and writes a
  `docker-compose.override.yml` next to `docker-compose.yml` adding the
  Windows-side mount. `docker compose` merges it automatically — no `-f` flag,
  nothing to do by hand.
- **Manual `docker compose`:** create a `docker-compose.override.yml` beside
  `docker-compose.yml` (replace `<you>` with your Windows username):

  ```yaml
  services:
    techlens-agent:
      volumes:
        - /mnt/c/Users/<you>/.ssh:/techlens/.win_ssh:ro
  ```

- **With `run-docker.sh`:** set `WIN_USER`; the script adds the mount only when
  `/mnt/c/Users/$WIN_USER/.ssh` exists:

  ```bash
  WIN_USER=<your-windows-username> ./run-docker.sh -d
  ```

On non-WSL machines no override is created and no Windows-side mount is attempted.