Metadata-Version: 2.4
Name: cloudcost-cli
Version: 0.1.0
Summary: CloudCost CLI for Terraform cost estimates and GitHub pull request cost automation.
Author: CloudCost AI
Project-URL: Homepage, https://cloudcost.live
Project-URL: Documentation, https://cloudcost.live/docs/cloudcost-cli.html
Project-URL: Source, https://github.com/zedxod/cloudcost
Keywords: finops,terraform,infracost,github,cloud-cost,llm-cost
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic-settings>=2.5.0
Provides-Extra: server
Requires-Dist: fastapi>=0.115.0; extra == "server"
Requires-Dist: litellm[proxy]>=1.85.0; extra == "server"
Requires-Dist: prisma>=0.15.0; extra == "server"
Requires-Dist: psycopg[binary]>=3.2.0; extra == "server"
Requires-Dist: PyJWT[crypto]>=2.9.0; extra == "server"
Requires-Dist: python-multipart>=0.0.12; extra == "server"
Requires-Dist: uvicorn[standard]>=0.30.0; extra == "server"
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: fastapi>=0.115.0; extra == "dev"
Requires-Dist: litellm[proxy]>=1.85.0; extra == "dev"
Requires-Dist: prisma>=0.15.0; extra == "dev"
Requires-Dist: psycopg[binary]>=3.2.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: PyJWT[crypto]>=2.9.0; extra == "dev"
Requires-Dist: python-multipart>=0.0.12; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Requires-Dist: uvicorn[standard]>=0.30.0; extra == "dev"

# CloudCost AI

CloudCost AI is a developer-first FinOps MVP. It has two mechanisms:

1. An AI spend proxy powered by LiteLLM.
2. A GitHub App webhook that estimates Terraform cost deltas in pull requests with Infracost.

The landing page is served from `index.html`; the working backend is in `backend/app`.

## Get Started

The first-run path is modeled around a local CLI estimate, then GitHub automation:

```powershell
.\.venv\Scripts\python -m pip install -e .
cloudcost go
```

Run `cloudcost go` from a Terraform project directory. It runs the local estimate, then opens the GitHub App manifest install flow with the required webhook, events, and permissions already filled in. If a team only wants the local estimate, `cloudcost` still works by itself. Power users can also run `cloudcost setup`, `cloudcost doctor`, `cloudcost connect-github`, or `cloudcost analyze --plan tfplan.json` directly.

When the package is published, users should install it with:

```bash
pipx install cloudcost-cli
cloudcost go
```

Publishing notes are in `docs/publishing-pypi.md`.

The signed-in dashboard sidebar links to `/get-started`, a docs-style first-run guide with macOS, Windows, and Linux command tabs that update the CLI install, setup, Terraform, CI, and GitHub steps. The left docs rail links to styled HTML docs for CloudCost CLI, VPS install, and pricing API reference.

## Cloudflare Pages Landing Page

For a free public early-access page, deploy the static pages plus the Pages Functions:

- `functions/api/waitlist.js`
- `functions/api/auth/*`
- `functions/api/dashboard/summary.js`
- `package.json`

Use `npm run build` and set the Cloudflare Pages output directory to `dist`; the build script copies `index.html`, `assets/`, `docs/`, `/login`, `/signup`, `/dashboard`, `/get-started`, and `/tester-dashboard`.

Set `DATABASE_URL`, `AUTH_SECRET`, `RESEND_API_KEY`, and `FROM_EMAIL` in Cloudflare Pages environment variables. The Functions store early-access emails, users, OTPs, and browser sessions in Neon, and send Resend confirmations without running the Python backend.

See `docs/cloudflare-pages-waitlist.md`.

## One-Click VPS Path

For a customer-facing install, use the VPS bundle instead of the local debug path.

Prerequisites:

- Ubuntu VPS with ports `80` and `443` open
- A domain pointed at the VPS
- Docker, or approval for the bootstrap script to install Docker
- A pricing mode:
  - Recommended: CloudCost hosted pricing key
  - Advanced: Infracost API key for syncing a self-hosted pricing database

Run:

```bash
git clone YOUR_REPO_URL cloudcost-ai
cd cloudcost-ai
bash scripts/bootstrap-vps.sh
```

The script generates local secrets, creates `.env`, starts Caddy HTTPS, backend, LiteLLM, and Postgres. If the customer chooses strict self-hosted pricing, it also initializes and runs the local Infracost pricing API. It then prints:

- Dashboard: `https://YOUR_DOMAIN/dashboard`
- Tester runbook: `https://YOUR_DOMAIN/tester-dashboard`
- GitHub install page: `https://YOUR_DOMAIN/install/github`
- Webhook URL: `https://YOUR_DOMAIN/api/github/webhook`

Open the GitHub install page and click **Install on GitHub**. GitHub creates a customer-owned GitHub App from CloudCost's manifest and redirects back with the App ID, webhook secret, and private key. CloudCost stores those on the VPS automatically.

After setup, run:

```bash
bash scripts/doctor-vps.sh
```

This path is the intended customer install path. The longer local setup below remains for development and debugging.

## Architecture

```text
Browser
  -> FastAPI backend
     -> static landing page
     -> waitlist endpoint
     -> GitHub webhook endpoint
     -> LiteLLM admin wrapper
     -> metadata-only usage ingest

LLM clients
  -> LiteLLM proxy :4000
     -> OpenAI / Anthropic / other providers
     -> metadata-only callback
     -> FastAPI /api/llm/usage

GitHub pull_request webhook
  -> HMAC validation
  -> installation access token
  -> locate Terraform plan JSON
  -> Infracost Plan JSON API
  -> upsert PR comment
```

## Quick Start

1. Create local environment config:

```powershell
Copy-Item .env.example .env
```

2. Fill `.env` with:

- `DATABASE_URL`, preferably a pooled Neon Postgres URL with `sslmode=require`
- `GITHUB_APP_ID` or `GITHUB_CLIENT_ID`
- `GITHUB_PRIVATE_KEY_PATH`
- `GITHUB_WEBHOOK_SECRET`
- `INFRACOST_API_KEY`
- `LITELLM_MASTER_KEY`
- at least one provider key, such as `OPENAI_API_KEY`

Put the downloaded GitHub App private key at `secrets/github-app-private-key.pem`.
Docker Compose mounts `./secrets` into the backend container as `/app/secrets`.

`DATABASE_URL` is shared by LiteLLM and the backend. LiteLLM uses it for virtual keys and spend/auth state; the backend creates `cloudcost_waitlist`, `cloudcost_usage_events`, `cloudcost_users`, `cloudcost_email_otps`, and `cloudcost_sessions` tables for app data. Set `APP_DATABASE_URL` if you want those app tables in a separate database.

The waitlist endpoint accepts only an email address, stores one row per email, and uses `data/waitlist.jsonl` only when no Postgres URL is configured. Set `SMTP_HOST` and `SMTP_FROM_EMAIL` if you want waitlist signups to receive a confirmation email.

For app signups, set `AUTH_SECRET` to a long random value. New accounts verify a mail OTP before `/dashboard` creates a session.

3. Start the stack:

```powershell
docker compose up --build
```

If Docker Desktop is not installed, run the backend and LiteLLM locally instead:

```powershell
.\.venv\Scripts\python -m pip install -e ".[dev]"
.\.venv\Scripts\python -m pip install "litellm[proxy]" prisma
.\scripts\start-litellm-local.ps1
```

In a second terminal:

```powershell
.\.venv\Scripts\python -m uvicorn backend.app.main:app --reload --port 8000
```

For the local no-Docker path, set `LITELLM_BASE_URL=http://127.0.0.1:4000` in `.env`.

4. Open:

- Landing page: `http://localhost:8000`
- Sign in: `http://localhost:8000/login`
- Sign up: `http://localhost:8000/signup`
- Dashboard: `http://localhost:8000/dashboard`
- Get started: `http://localhost:8000/get-started`
- Tester runbook: `http://localhost:8000/tester-dashboard`
- Backend health: `http://localhost:8000/healthz`
- Mechanism map: `http://localhost:8000/api/mechanism`
- LiteLLM proxy: `http://localhost:4000`

## GitHub App Setup

Register a GitHub App with:

- Webhook URL: `https://YOUR_DOMAIN/api/github/webhook`
- Webhook secret: same value as `GITHUB_WEBHOOK_SECRET`
- Subscribe to only `pull_request`
- Repository permissions:
  - Contents: read
  - Pull requests: read and write
  - Issues: write
  - Metadata: read

The backend handles `opened`, `reopened`, `synchronize`, and `ready_for_review` pull request actions.

## Terraform Plan JSON

The MVP looks for one of these files in the pull request files first, then in the repository tree at the PR head SHA:

- `plan.json`
- `tfplan.json`
- `terraform-plan.json`
- `infracost-plan.json`
- files ending in `.tfplan.json` or `.plan.json`

Generate a compatible file with Terraform:

```powershell
terraform init
terraform plan -out tfplan.binary
terraform show -json tfplan.binary > plan.json
```

When found, the backend runs the local Infracost CLI against the plan JSON, parses `diffTotalMonthlyCost`, and creates or updates one CloudCost AI pull request comment. In the default self-hosted setup, the plan JSON stays inside your backend runtime; the CLI only talks to your Cloud Pricing API for price lookups.

## Self-Hosted Infracost

The default project mode is:

```env
INFRACOST_MODE=cli
INFRACOST_CLI_PATH=infracost
INFRACOST_PRICING_API_ENDPOINT=http://127.0.0.1:4001
```

That means CloudCost AI does not call the hosted Infracost Plan JSON API. Instead:

1. You self-host the Infracost Cloud Pricing API.
2. The backend runs `infracost breakdown --path plan.json --format json`.
3. The Infracost CLI requests prices from `INFRACOST_PRICING_API_ENDPOINT`.
4. The backend comments the result on the pull request.

For local Windows development, install the Infracost CLI and make sure `infracost` is on PATH. For Docker builds, the backend image installs the CLI automatically.

See `/docs/pricing-api.html` for the full setup path.

## LiteLLM Spend Proxy

LiteLLM requires `DATABASE_URL` for persistent virtual keys, budgets, spend logs, and auth state. With Neon, use the pooled connection string from the Neon dashboard:

```env
DATABASE_URL=postgresql://USER:PASSWORD@HOST/neondb?sslmode=require&channel_binding=require
```

Create a virtual key through CloudCost AI:

```powershell
Invoke-RestMethod -Method Post http://localhost:8000/api/llm/keys `
  -ContentType 'application/json' `
  -Body '{
    "user_id": "ada@example.com",
    "team_id": "platform",
    "models": ["gpt-5-mini"],
    "max_budget": 10,
    "budget_duration": "30d",
    "metadata": {"service": "billing"}
  }'
```

Use the returned key against LiteLLM:

```powershell
$env:OPENAI_API_KEY = "sk-returned-virtual-key"
```

Point OpenAI-compatible clients at:

```text
http://localhost:4000/v1
```

On the VPS bundle, Caddy exposes LiteLLM through the same HTTPS origin:

```text
https://YOUR_DOMAIN/llm/v1
```

LiteLLM enforces the budgets and forwards metadata-only usage records to `/api/llm/usage`. The custom callback deliberately omits prompts, messages, responses, choices, and completions.

## Local Development

Install dependencies:

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
```

Run tests:

```powershell
pytest
```

Run the backend without Docker:

```powershell
uvicorn backend.app.main:app --reload --port 8000
```

## Important Limits

This is an MVP scaffold, not a production billing system yet.

- Real GitHub, Infracost, and LLM provider credentials are required for end-to-end live calls.
- The JSONL stores in `data/` are for local validation; replace them with a database before production.
- For production GitHub traffic, put the backend behind HTTPS and keep the webhook secret/private key out of the repo.
- For production LiteLLM, pin image versions, rotate master keys, and use a managed Postgres database.
