Metadata-Version: 2.5
Name: virtualdojo
Version: 0.29.0
Summary: Command-line interface for VirtualDojo CRM
Project-URL: Homepage, https://github.com/Quote-ly/virtualdojo_cli
Project-URL: Documentation, https://github.com/Quote-ly/virtualdojo_cli#readme
Project-URL: Repository, https://github.com/Quote-ly/virtualdojo_cli
Project-URL: Issues, https://github.com/Quote-ly/virtualdojo_cli/issues
Author-email: VirtualDojo Team <support@virtualdojo.com>
License-Expression: MIT
License-File: LICENSE
Keywords: automation,cli,crm,salesforce-alternative,virtualdojo
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Groupware
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx>=0.25.0
Requires-Dist: keyring>=24.0.0
Requires-Dist: platformdirs>=4.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: rich>=13.0.0
Requires-Dist: tomli-w>=1.0.0
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
Requires-Dist: typer[all]>=0.9.0
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# VirtualDojo CLI

Command-line interface for VirtualDojo CRM - interact with your CRM data, manage records, and automate workflows from the terminal.

## Features

- **Authentication**: SSO or email/password login; multiple server/tenant profiles
- **Record Management**: Full CRUD on any object, with powerful filtering
- **Bulk Operations**: Bulk create, update, delete, and upsert from CSV or JSON files
- **Search & Export**: Global full-text search and CSV/JSON export of records
- **Relational Migration**: Tree export/import of records with their related children
- **Schema Management**: Explore objects/fields/picklists and create/update/delete them
- **Config-as-Code**: Export/import schema, page layouts, PDF templates, data guardrails, webhooks, and flows between tenants
- **Admin**: Manage users, roles, permission profiles, and view usage/billing
- **Recycle Bin**: Restore soft-deleted records, purge permanently, and read the immutable removal-event audit trail
- **DojoScript**: Author, test, run, and trigger server-side automation classes
- **File Management**: Upload, download, and manage files with progress tracking
- **AI, SQL, Logs & System**: AI chat, SQL queries, log viewing, and health monitoring
- **Rich Output**: Tables, JSON, and YAML; raw API access and shell completion

## Installation

### From PyPI (recommended)

```bash
pip install virtualdojo
```

Or with [pipx](https://pypa.github.io/pipx/) (recommended for CLI tools):

```bash
pipx install virtualdojo
```

### From Source

```bash
git clone https://github.com/Quote-ly/virtualdojo_cli.git
cd virtualdojo_cli
pip install -e .
```

## Quick Start

### 1. Login

```bash
# Login to default production server (prompts for email, password)
vdojo login

# Login to local development server
vdojo login --local
vdojo login -l

# Login to specific server
vdojo login --server localhost:8000 --tenant my-tenant
vdojo login -s dev -t my-tenant

# Non-interactive login (for CI/CD - use environment variables!)
export VIRTUALDOJO_EMAIL=ci@example.com
export VIRTUALDOJO_PASSWORD=...
export VIRTUALDOJO_TENANT=my-company
vdojo login
```

### 2. Check Connection

```bash
vdojo whoami
```

### 3. List Records

```bash
# List accounts
vdojo records list accounts

# List with filtering
vdojo records list opportunities --filter "stage_ne=closed,amount_gte=10000"

# Output as JSON
vdojo records list contacts --format json
```

## Commands

Command groups at a glance (run `vdojo <group> --help` for details):

| Group | What it does |
|---|---|
| `auth` (+ `login`/`logout`/`whoami`) | Authentication and API key management |
| `records` | Record CRUD, bulk ops, upsert, search, export, tree export/import |
| `schema` | Objects/fields: discover, create/update/delete, export/import |
| `layouts` | Page layout export/import |
| `pdf` | PDF template list/export/import |
| `guardrails` | Validation & duplicate rules: list/create/export/import |
| `webhooks` | Webhook subscriptions: CRUD + export/import |
| `flows` | Automation flows: CRUD + export/import |
| `dojoscript` | Server-side classes, tests, triggers; compile/run |
| `users` / `roles` / `permissions` | User, role, and permission-profile admin |
| `usage` | Token usage and billing |
| `files` | Upload, download, and manage files |
| `ai` | AI chat and conversations |
| `sql` | SQL query operations |
| `logs` | Log viewing and management |
| `system` | System health and monitoring |
| `support` | Cross-tenant support tooling |
| `config` | CLI configuration and profiles |
| `api` | Raw API requests |
| `open` | Open the web app / a record in the browser |

### Authentication

```bash
# Login (shortcuts available at top level)
vdojo login                                    # Default server, prompts for details
vdojo login --local                            # Local development (localhost:8000)
vdojo login -s dev -t my-tenant            # Dev server
vdojo login --server api.mycompany.com -t prod # Custom server

# Server shortcuts:
#   --local, -l          → http://localhost:8000
#   --server local       → http://localhost:8000
#   --server dev         → dev server
#   --server production  → production server

# Check current user
vdojo whoami

# Logout
vdojo logout

# Manage API keys
vdojo auth api-key list
vdojo auth api-key create --name "CI Pipeline" --expires 90
vdojo auth api-key revoke KEY_ID
```

### Records

```bash
# List records
vdojo records list accounts
vdojo records list accounts --limit 100 --filter "status=active"

# Get single record
vdojo records get accounts acc-123

# Create record
vdojo records create accounts --data '{"name": "Acme Corp"}'
vdojo records create tasks --set "name=Follow up" --set "status=pending"

# Update record
vdojo records update accounts acc-123 --set "status=active"

# Delete record
vdojo records delete accounts acc-123

# Count records
vdojo records count opportunities --filter "stage=negotiation"

# Search across objects (global full-text search)
vdojo records search "acme"
vdojo records search "john@example.com" --objects contacts,leads

# Export to CSV/JSON (paginates through all matches)
vdojo records export accounts --out accounts.csv
vdojo records export opportunities --filter "stage_ne=closed" --out open.json --format json
vdojo records export contacts --columns id,email,name --out contacts.csv

# Relational tree export/import (a record plus its related children)
vdojo records tree-export accounts --id acc-123 --out tree.json
vdojo records tree-export accounts --filter "industry=Tech" --children contacts,opportunities --out tree.json
vdojo records tree-import tree.json --profile other-tenant   # recreates + relinks children
```

#### Bulk operations

Pass a multi-row **CSV** or **JSON array** to `--file` and the command uses the
bulk endpoint. A single JSON object (or `--data`/`--set`) still operates on one
record, so existing usage is unchanged.

```bash
# Bulk create from a CSV (header row = field names) or JSON array
vdojo records create accounts --file accounts.csv
vdojo records create contacts --file contacts.json

# Bulk update — each row/object must include the record id
vdojo records update accounts --file updates.csv
vdojo records update accounts --file updates.json --id-field record_id

# Bulk delete — by id list or from a file of records/ids
vdojo records delete accounts --ids acc-1,acc-2,acc-3 --force
vdojo records delete accounts --file stale.csv --force

# Bulk hard delete — permanent, bypasses the recycle bin (see below)
vdojo records delete accounts --ids acc-1,acc-2 --hard --force

# Upsert — update existing rows, create the rest, matched on --external-id
vdojo records upsert accounts --file accounts.csv --external-id id
vdojo records upsert contacts --file contacts.csv --external-id email
```

Common options for the bulk paths:

- `--batch-size N` — records processed per batch
- `--stop-on-error` — abort the batch on the first failure (default: continue)
- `--errors-file errors.json` — write failed rows (with reasons) for re-submission

Bulk operations report a per-run summary and exit non-zero if any record fails.
Upsert has no native server endpoint: it looks up existing records by
`--external-id`, then issues a bulk update plus a bulk create. The match field
should be unique — rows matching more than one record are reported as errors.

### Recycle bin & permanent deletion

`vdojo records delete` is a **soft** delete: the record moves to the recycle
bin and can be restored. `--hard` destroys it permanently.

```bash
# Soft delete (recoverable)
vdojo records delete accounts acc-123

# Permanent — soft-deletes, then purges. Prompts you to type the object name.
vdojo records delete accounts acc-123 --hard
vdojo records delete leads --ids l-1,l-2 --hard --force     # bulk, unattended
```

A hard delete requires the `delete_all_data` (or `system_administrator`) admin
permission. It clears every live copy of the record — database row, Redis
caches, sharing grants, cached report results and stored files — and the server
writes an **immutable removal event** recording who destroyed what, when, and
from where. If a store could not be cleared, the CLI warns and names it; the
full per-store receipt is in `--format json`.

Manage the bin itself with `vdojo recycle-bin`:

```bash
vdojo recycle-bin list                          # what is recoverable
vdojo recycle-bin list --object accounts --all  # every user's (admin)
vdojo recycle-bin objects                       # supported object types

vdojo recycle-bin restore accounts acc-123
vdojo recycle-bin restore accounts --ids acc-1,acc-2

vdojo recycle-bin purge accounts acc-123        # destroy something already binned
vdojo recycle-bin purge accounts --ids acc-1,acc-2 --force
```

Bulk restore/purge are chunked at the server's 200-record limit automatically.

#### Removal events (audit trail)

```bash
vdojo recycle-bin events                        # what was destroyed, by whom
vdojo recycle-bin events --object accounts --days 30
vdojo recycle-bin events --outcome failure      # denied or failed attempts
vdojo recycle-bin events --format json | jq '.events[].stores_purged'

vdojo recycle-bin verify                        # is the trail intact?
```

Events are append-only and hash-chained per tenant, so an edited or deleted
event breaks the chain. `verify` recomputes it and exits non-zero if it is
broken, naming the first bad event — treat that as an audit-integrity
incident, not a CLI error.

### Schema

```bash
# List all objects
vdojo schema objects
vdojo schema objects --type custom  # Only custom objects

# Describe an object
vdojo schema describe accounts

# List fields
vdojo schema fields opportunities
vdojo schema fields contacts --required  # Only required fields

# View picklist values
vdojo schema picklists opportunities --field stage
```

#### Managing objects and fields (admin)

Create, update, and delete custom objects and fields directly via the API.
Field commands auto-route by object type: a custom object (`_co`) uses the
custom-object endpoint, anything else is treated as a standard object.

```bash
# Custom objects
vdojo schema create-object --label "Project"        # api_name derived from label
vdojo schema create-object --label "Invoice" --api-name invoice \
    --name-field-type autonumber --autonumber-format "INV-{0000}"
vdojo schema update-object project_co --label "Projects (2026)"
vdojo schema delete-object project_co

# Fields (on standard or custom objects)
vdojo schema create-field accounts --label "Region" --type picklist \
    --picklist "East,West,Central"
vdojo schema create-field project_co --label "Budget" --type currency --required
vdojo schema create-field accounts --label "Primary Contact" --type lookup \
    --lookup-object contacts
vdojo schema update-field accounts region_cf --label "Sales Region"
vdojo schema delete-field accounts region_cf
```

These commands require admin permissions (`manage_custom_objects` /
`manage_custom_fields`).

#### Export / import (config-as-code)

Export an object's definition (custom object + its fields, or a standard
object's custom fields) to a JSON file, and recreate it elsewhere — useful for
migrating schema between tenants.

```bash
# Export
vdojo schema export project_co --out project.json     # custom object + fields
vdojo schema export accounts --out account_fields.json # standard object's custom fields

# Import (into another tenant via --profile)
vdojo schema import project.json -p other-tenant
vdojo schema import project.json --api-name project_copy      # rename on import
vdojo schema import account_fields.json --into accounts -p other-tenant
```

### Open in browser

```bash
vdojo open                     # open the web app home
vdojo open accounts            # open the accounts list
vdojo open accounts acc-123    # open a specific record
vdojo open --path /admin/users # open an explicit path
vdojo open accounts --print    # print the URL instead of launching
```

### Shell completion

```bash
vdojo --install-completion   # install completion for your shell
vdojo --show-completion      # print the completion script
```

### Users, roles & usage (admin)

```bash
# Users
vdojo users list
vdojo users lookup jane
vdojo users create -e jane@acme.com --firstname Jane --lastname Doe
vdojo users update <id> --title "VP Sales"
vdojo users deactivate <id>

# Roles
vdojo roles list
vdojo roles create --name "Sales Manager"
vdojo roles assign-permission <role_id> <permission_id>

# Token usage & billing
vdojo usage tokens --by-user --from 2026-06-01 --to 2026-06-30
vdojo usage billing      # license/billing status
vdojo usage licenses     # license counts per package
vdojo usage history      # billing history
```

User/role management requires the corresponding admin permissions.

### Page layouts (export/import)

Export a page layout to a portable JSON file and recreate it in another tenant.
The layout structure references fields and related objects by api_name, so it
moves cleanly between tenants. (Layout *assignments* — which profile/record
type sees a layout — are tenant-specific and not included.)

```bash
vdojo layouts list accounts
vdojo layouts export accounts --out account_layout.json     # default layout
vdojo layouts export accounts --all --out account_layouts.json
vdojo layouts import account_layout.json --profile other-tenant
vdojo layouts import account_layout.json --into accounts --name "Imported" --default
```

Imported layouts are created non-default unless you pass `--default`.

### PDF templates (export/import)

Export PDF/document templates (the `configuration` blob — layout, components,
styles, and `{{merge.fields}}`) to a portable JSON file and recreate them in
another tenant. Object associations are by api_name, so templates move cleanly.
(Template *assignments* — defaults/conditions per object/profile — are
tenant-specific and not included.)

```bash
vdojo pdf list
vdojo pdf list --object quotes --status active
vdojo pdf export --name "Standard Quote" --out quote_pdf.json
vdojo pdf export --object quotes --out quote_templates.json
vdojo pdf export --all --out all_pdf_templates.json
vdojo pdf import quote_pdf.json --profile other-tenant
vdojo pdf import quote_pdf.json --name "Quote PDF (copy)" --status draft
```

Each template keeps its source status on import unless you pass `--status`.

### Data guardrails

Validation rules (formula-based, block on save) and duplicate rules (dedupe
detection) for an object. List/create them, and export/import a portable bundle
of both types between tenants.

```bash
vdojo guardrails list accounts
vdojo guardrails create-validation accounts --name "Amount required" \
    --formula "ISBLANK({amount})" --error-message "Amount is required"
vdojo guardrails create-duplicate accounts --name "Dupe email" \
    --match email:exact --match name:fuzzy --action allow_warning

# Migration (both rule types in one file)
vdojo guardrails export accounts --out accounts_guardrails.json
vdojo guardrails import accounts_guardrails.json --into contacts

# Show the file format for import (ready to edit)
vdojo guardrails template --out guardrails.sample.json
```

### DojoScript (server-side code)

Manage server-side DojoScript classes (Python automation), their tests and
triggers, and execute/inspect them — the VirtualDojo equivalent of Apex.

```bash
# Classes
vdojo dojoscript classes list [--type standard] [--active]
vdojo dojoscript classes create --name MyClass --type standard --file my_class.py
vdojo dojoscript classes compile --file my_class.py --type standard   # dry-run
vdojo dojoscript classes activate <id>      # requires compiled + tests passed

# Run + logs
vdojo dojoscript run <api_name> --params '{"x": 1}'
vdojo dojoscript executions <class_id>

# Tests
vdojo dojoscript tests create <class_id> --name "ok" --target-method execute \
    --expected success --data '{"params": {}}'
vdojo dojoscript tests run <class_id>

# Triggers (bind a trigger class to an object event)
vdojo dojoscript triggers create --class <id> --object accounts --event before_insert
```

### Flows (automation)

Read/create/update/list/delete automation flows, and migrate them between
tenants. A flow's definition (nodes/edges/variables) references objects and
fields by api_name, so it's portable; imported flows are created as drafts.

```bash
vdojo flows list [--status active] [--type record_trigger]
vdojo flows get <id>                       # full definition (JSON)
vdojo flows create --file flow.json [--name "Copy"]
vdojo flows update <id> --status active     # or --file to replace the definition
vdojo flows delete <id>

# Migration
vdojo flows export --name "Lead router" --out flow.json   # or --all
vdojo flows import flows.json --profile other-tenant
```

### Webhooks

Manage outbound webhook subscriptions and migrate their config between tenants.
Webhooks fire on record events (e.g. `record.created:accounts`). The signing
secret is shown only once at creation and can't be exported, so an import
issues a fresh secret per webhook.

```bash
vdojo webhooks list
vdojo webhooks events --object accounts          # valid --event values
vdojo webhooks create --url https://example.com/in \
    --event record.created:accounts --event record.updated:accounts
vdojo webhooks update <id> --status paused
vdojo webhooks delete <id>

# Migration (config only — no secrets)
vdojo webhooks export --out webhooks.json
vdojo webhooks import webhooks.json --secrets-out new_secrets.json
```

### Support & impersonation

Two flows: a tenant admin with the `login_as_user` permission impersonating a
user in their own tenant, and cross-tenant support impersonation, where a
customer grants a support tenant time-boxed access to their account.

The session token is kept in a sibling connection profile named `<parent>+imp`,
which becomes the default while the session is live — your own credentials are
never overwritten, and `vdojo support end` restores them.

Grants and sessions carry a **channel** — `web`, `cli`, or `mcp`. A session
occupies one slot per (admin, channel, customer tenant), so a `cli` session and
a browser `web` session coexist in the same tenant instead of evicting each
other. `vdojo support grants create` always issues a `cli` grant, and the
session inherits that channel from the grant, so no API key is required — an
ordinary `vdojo login` profile is enough.

Grants issued from the browser are `web`-channel and land in the same slot as
your own browser session, so a `cli` grant is what you want for terminal work.
`vdojo support inbox` shows each grant's channel.

```bash
# Customer side — grant a support tenant access (always 'cli' channel)
vdojo support grants tenants
vdojo support grants create --to <tenant_id> --duration 1d
vdojo support grants list
vdojo support grants revoke <grant_id>

# Support side — use a grant
vdojo support inbox
vdojo support start <grant_id>

# Same-tenant admin
vdojo support login-as <user_id>

# While impersonating
vdojo support whoami        # who you're acting as, token + session budget left
vdojo support renew         # extend the session (usually automatic)
vdojo support end           # restore your own profile
```

Token lifetime is set by the server, not the CLI, and is read from the
`expires_at` on the response — so a server-side policy change takes effect
without a CLI release. An in-use session is renewed automatically when the
current token is about to lapse, up to the server's hard maximum session
duration. Once that ceiling is hit the server refuses renewal and ends the
session; `vdojo support end` then returns you to your own profile.

### Permission profiles

CRM permission profiles (Salesforce-style) — distinct from the CLI connection
profiles selected with `-p/--profile`.

```bash
vdojo permissions profiles list
vdojo permissions profiles create --name "Sales Rep"
vdojo permissions assign-user <profile_id> <user_id>

# Object-level permissions
vdojo permissions object-perms <profile_id>
vdojo permissions set-object <profile_id> accounts --crud
vdojo permissions set-object <profile_id> contacts --read --edit
```

### Files

```bash
# List files and folders
vdojo files list
vdojo files list --folder folder-123       # List folder contents
vdojo files list --type image              # Filter by type

# Get file info
vdojo files info file-123
vdojo files info file-123 --format json

# Upload files
vdojo files upload ./report.pdf                      # Upload to root
vdojo files upload ./report.pdf -f folder-123        # Upload to folder
vdojo files upload ./data/ --recursive               # Upload directory

# Download files
vdojo files download file-123                        # Download to current dir
vdojo files download file-123 -o ./downloads/        # Download to directory
vdojo files download file-123 -o ./report.pdf        # Download with name

# Delete files
# Default is a soft delete: the file moves to the recycle bin and stays
# restorable until its retention window expires.
vdojo files delete file-123
vdojo files delete folder-456 --recursive --force   # folder + its contents
vdojo files delete file-123 --permanent             # destroy outright, no undo

# Deleted files live in the recycle bin as 'content_documents'
vdojo recycle-bin list --object content_documents
vdojo recycle-bin restore content_documents file-123

# Create folders
vdojo files mkdir "New Folder"
vdojo files mkdir "Reports" --parent folder-123

# Move, rename, copy
vdojo files move file-123 --to folder-456
vdojo files rename file-123 --name "new-name.pdf"
vdojo files copy file-123 --to folder-456

# Share files
vdojo files share file-123 --public                  # Generate public link
vdojo files share file-123 --user user-456           # Share with user
vdojo files share file-123 --user user-456 --permission edit
vdojo files unshare file-123 --user user-456
vdojo files shares file-123                          # List shares

# Link files to records
vdojo files link file-123 --object accounts --record acc-456
vdojo files unlink file-123 --link link-789
vdojo files links file-123                           # List links

# Search files
vdojo files search "quarterly report"
vdojo files search "report" --type document --created-after 2024-01-01

# Storage info
vdojo files storage
```

### Configuration

```bash
# Show current config
vdojo config show

# Manage profiles
vdojo config profile list
vdojo config profile add dev --server https://dev.virtualdojo.com --tenant test
vdojo config profile use dev
vdojo config profile remove old-profile

# Change settings
vdojo config set default_limit 100
vdojo config set output_format json
```

## Filter Operators

When using `--filter`, you can use these operators:

| Operator | Description | Example |
|----------|-------------|---------|
| (none) | Equals | `status=active` |
| `_ne` | Not equals | `stage_ne=closed` |
| `_gt` | Greater than | `amount_gt=10000` |
| `_gte` | Greater than or equal | `amount_gte=10000` |
| `_lt` | Less than | `amount_lt=1000` |
| `_lte` | Less than or equal | `amount_lte=1000` |
| `_contains` | Contains text | `name_contains=Acme` |
| `_startswith` | Starts with | `name_startswith=A` |
| `_endswith` | Ends with | `email_endswith=@corp.com` |
| `_in` | In list | `status_in=active\|pending` or `status_in="active,pending"` |
| `_isnull` | Is null | `email_isnull=true` |

Combine multiple filters with commas:

```bash
vdojo records list opportunities --filter "stage_ne=closed,amount_gte=10000,owner_contains=john"
```

## Output Formats

All commands support multiple output formats:

```bash
# Table (default) - human-readable
vdojo records list accounts

# JSON - machine-readable
vdojo records list accounts --format json

# YAML - configuration-friendly
vdojo records list accounts --format yaml
```

## Multiple Profiles

Manage connections to different environments:

```bash
# Add profiles
vdojo config profile add production --server https://api.virtualdojo.com --tenant prod
vdojo config profile add dev --server https://dev.virtualdojo.com --tenant dev
vdojo config profile add local --server http://localhost:8000 --tenant dev

# Switch default profile
vdojo config profile use production

# Use a specific profile for one command
vdojo records list accounts --profile dev
```

## Configuration

Configuration is stored in:
- **Linux/macOS**: `~/.config/virtualdojo/config.toml`
- **Windows**: `%APPDATA%\virtualdojo\config.toml`

Credentials are stored separately with restricted permissions:
- **Linux/macOS**: `~/.config/virtualdojo/credentials.toml`
- **Windows**: `%APPDATA%\virtualdojo\credentials.toml`

## Security

### Credential Storage

The CLI stores authentication tokens securely:

1. **System Keyring (Recommended)**: When available, tokens are stored in your operating system's secure credential storage:
   - **macOS**: Keychain
   - **Linux**: Secret Service (GNOME Keyring, KWallet)
   - **Windows**: Windows Credential Manager

2. **Fallback File Storage**: If no system keyring is available, tokens are stored in `credentials.toml` with restricted file permissions (`0600` - owner read/write only).

**Recommendations:**
- Use full-disk encryption on your machine
- On shared systems, ensure your home directory is not accessible to other users
- Regularly rotate API keys via `vdojo auth api-key create` / `vdojo auth api-key revoke`

### Environment Variables for CI/CD

For automated workflows, use environment variables instead of command-line arguments to avoid exposing credentials in shell history and process listings:

```bash
# Set credentials via environment (secure)
export VIRTUALDOJO_EMAIL=ci@example.com
export VIRTUALDOJO_PASSWORD=your-password
export VIRTUALDOJO_TENANT=your-tenant-id
export VIRTUALDOJO_SERVER=https://api.virtualdojo.com

# Run commands without exposing secrets
vdojo login
vdojo records list accounts
```

Available environment variables:
| Variable | Description |
|----------|-------------|
| `VIRTUALDOJO_PASSWORD` | Password (for non-interactive login) |
| `VIRTUALDOJO_EMAIL` | Email address |
| `VIRTUALDOJO_TENANT` | Tenant ID or subdomain |
| `VIRTUALDOJO_SERVER` | Server URL |

### HTTPS Connections

The CLI uses HTTPS by default for all production connections. When connecting to HTTP endpoints (like `localhost` for development), a warning is displayed:

```
! Using insecure HTTP connection to http://localhost:8000.
  Credentials will be transmitted in plaintext.
```

**Never use HTTP for production environments.**

### Security Best Practices

1. **Don't commit credentials** - Never commit `.env` files or credentials to version control
2. **Rotate credentials** - Regularly rotate API keys, especially after team member departures
3. **Use environment variables** - Prefer `VIRTUALDOJO_EMAIL`/`VIRTUALDOJO_PASSWORD` over interactive prompts in scripts
4. **Audit access** - Review API key usage via `vdojo auth api-key list`

API keys authenticate **external integrations**, not the CLI. `vdojo login`
cannot consume one; use SSO or email/password.

## Development

### Setup

```bash
# Clone repository
git clone https://github.com/Quote-ly/virtualdojo_cli.git
cd virtualdojo_cli

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run linting
ruff check src/
black --check src/
```

### Running Locally

```bash
# Run CLI directly
python -m virtualdojo --help

# Or after installing
vdojo --help
```

## Requirements

- Python 3.10+
- A VirtualDojo CRM instance to connect to

## Changelog

### v0.29.0 (2026-09-15)

**Multi-tenant login sends a real credential.**

`vdojo login` for a user with access to more than one tenant completes in two
steps: `/auth/login-multi-tenant` returns the tenant list, then
`/auth/select-tenant` exchanges a choice for a session. The CLI sent
`{tenant_id, user_id}` on that second call — no credential of any kind. The
server is closing that hole (`virtualdojo-inc/virtualdojo#1645`): step 1 now
mints a single-use, 300-second `selection_token` bound to the tenants the
password actually authenticated for, and once `AUTH_STRICT_LOGIN_ENFORCEMENT`
is enabled the legacy body is refused outright.

- `vdojo login` sends `selection_token` when the server issues one, and falls
  back to `user_id` when it does not — so one build works against servers with
  the enforcement on or off. Without this, enabling it locks every
  multi-tenant user out of the CLI.
- An expired or already-spent token now reports what happened and how to
  recover, instead of surfacing a bare 401. The token is single-use with a
  short TTL, so pausing at the tenant prompt is enough to hit it.
- A non-JSON error body (a proxy returning HTML, say) no longer crashes the
  error path.

This does **not** add TOTP/MFA support to the CLI, which remains a separate
gap tracked on #1645.

### v0.28.0 (2026-09-15)

**File deletion: recursive, permanent, and honest about what it did.**

- `vdojo files delete --recursive` deletes a folder's contents along with the
  folder. Without it the server leaves the children behind, which is what the
  CLI was silently doing for every folder delete until now.
- `vdojo files delete --permanent` (alias `--hard`) destroys the file outright
  instead of moving it to the recycle bin — versions, links, permissions,
  embeddings and the stored object all go. Like `records delete --hard`, it
  requires typing `file`/`folder` to confirm unless `--force` is passed, and
  refuses outright when there is no one to answer the prompt.
- A default (soft) delete now says the file moved to the recycle bin and prints
  the `recycle-bin restore content_documents <id>` command to undo it, instead
  of reporting "Deleted" for something that is still restorable.
- Fixed the confirmation prompt showing the file's UUID instead of its name:
  the API returns the name in `title`, which this command alone was not reading.
- `client.delete()` accepts `params=` so DELETE endpoints with query flags can
  be called.
- `vdojo files upload` reports the new file's real ID instead of `unknown` —
  the API nests it under `content_document`.
- A single `recycle-bin purge` now gets the same 120s timeout the bulk path
  already used, instead of the 30s default. Permanently deleting a file
  cascades child rows and clears the object from storage, which measured ~45s
  on dev — so the old ceiling reported "Request timed out" for purges that had
  actually succeeded server-side. `files delete --permanent` gets the same
  headroom.
- When a destructive delete does time out, the CLI now says the work may still
  have completed and prints the command to check, rather than implying nothing
  happened. A timeout abandons the response; it does not cancel the request.
- Suggested commands print via a new `print_command` helper that leaves
  wrapping to the terminal. Rich was hard-wrapping them mid-token, so a copied
  `recycle-bin restore ...` line came back broken.

Deleted files appear in the recycle bin as the `content_documents` object:

```bash
vdojo recycle-bin list --object content_documents
vdojo recycle-bin restore content_documents <file-id>
```

### v0.27.0 (2026-09-01)

**Hard delete, and the recycle bin from the terminal.**

- `vdojo records delete --hard` destroys a record permanently: it soft-deletes,
  then purges it from the recycle bin. Single and bulk (`--ids`/`--file`) both
  supported; bulk purges are chunked at the server's 200-record limit, and only
  records that were actually purged count as deleted, so a record left sitting
  in the bin is reported as a failure rather than a success.
- Without `--force`, a hard delete requires typing the object name — a y/n
  prompt is too easy to answer by reflex for something with no undo. With no
  answer available (piped input, CI), it refuses instead of destroying.
- If the server reports that a store could not be cleared, the CLI warns and
  names it; the full per-store receipt is available with `--format json`.
- New `vdojo recycle-bin` group: `list`, `objects`, `restore`, `purge`,
  `events`, `verify`.
- `vdojo recycle-bin events` reads the server's immutable removal-event trail
  (who destroyed what, when, from where, and what it cleared), including denied
  attempts via `--outcome failure`. `vdojo recycle-bin verify` recomputes the
  trail's per-tenant hash chain and exits non-zero if it has been tampered with.

Hard delete and the event trail require the `delete_all_data` (or
`system_administrator`) admin permission.

### v0.26.0 (2026-08-14)

**Removed: API-key login.**

`vdojo login --api-key` and `vdojo auth login --api-key` are gone, along with
the `VIRTUALDOJO_API_KEY` environment variable. The flow validated the key
against `/api/v1/users/me`, which accepts only session tokens and answers 403
for an API key, so it could never succeed — it was broken, not merely
redundant.

- Use SSO (the default) or email/password. For CI/CD, set `VIRTUALDOJO_EMAIL`
  and `VIRTUALDOJO_PASSWORD`.
- If `VIRTUALDOJO_API_KEY` is still exported, `vdojo login` now **fails with a
  clear error** rather than silently falling through to the browser SSO flow —
  which would leave a CI job waiting on a browser that never opens.
- **API keys themselves are unaffected.** They authenticate external
  integrations, and `vdojo auth api-key list/create/revoke` still manages them.

Eight tests covering the removed flow were deleted. Six of them had been
passing vacuously — one asserted nothing at all, and the others only confirmed
that an unrecognized option exits non-zero.

### v0.25.1 (2026-08-14)

**Bug Fixes:**
- `vdojo auth api-key create` printed `Key: -` and discarded the secret. The
  server returns it as `raw_key`; the CLI read `key`/`api_key`, names the API
  has never returned. Since only a SHA-256 hash is stored server-side, that
  response is the one and only time the plaintext exists — so every key created
  through the CLI was unrecoverable and had to be revoked and recreated. It now
  prints the key, and shows the key's channel. If a response ever arrives with
  no secret, the command warns and names the revoke command instead of printing
  the "save this now" banner over an empty value.
- `vdojo auth api-key list` always reported "No API keys found". The server
  returns `{api_keys, total_count, has_more}`; the CLI read `data`. The listing
  also now shows each key's prefix, channel, and active state.
- The tests covering both commands mocked response shapes the API never emits
  and asserted only the exit code, so they passed while both commands were
  broken. They now use the real shapes and assert on the rendered output.

### v0.25.0 (2026-08-14)

**Impersonation requires an interactive login:**
- `vdojo support start` and `vdojo support login-as` now refuse to run from an
  API-key profile, with a message telling you to `vdojo login`. Every
  impersonation endpoint authenticates by decoding a JWT and has no API-key
  branch, so a key previously produced an opaque 401 that the CLI reported as
  "your session may have expired" — the wrong advice. Detection is by the `sk-`
  prefix on the stored token, the same test the server applies.
- Removed the remaining support for the API-key impersonation path
  (`/impersonation/start-via-key`) and the error handling that existed only to
  serve it. The channel now comes from the grant, so binding it to a credential
  is no longer meaningful.
- Added `tests/test_cmd_support.py`, which pins the `support start` endpoint.
  Pointing it back at the API-key path fails the suite.

### v0.24.0 (2026-08-14)

**Per-channel support sessions:**
- `vdojo support start` now works from an ordinary login profile. The server
  derives the session channel from the grant, so a `cli` grant produces a `cli`
  session and no API key is involved. Previously this path could only mint
  `web`-channel sessions, which shared a slot with your browser session — the
  two evicted each other.
- `vdojo support grants create` issues grants on the `cli` channel. Grants
  created here do not affect existing `web` grants: the server scopes grant
  replacement per channel, so authorizing CLI access leaves browser access
  untouched.
- `vdojo support grants list` and `vdojo support inbox` show a `channel` column,
  so it's clear which grants are usable from the terminal.

Note: the web UI's grant dialogs do not yet offer a channel selector, so
browser-issued grants are always `web`. Use `vdojo support grants create` to
obtain a `cli` grant.

### v0.23.1 (2026-08-08)

**Bug Fixes:**
- `vdojo --version` reported `0.22.0` on the 0.23.0 release. The version is declared
  in two places — `pyproject.toml` (what PyPI serves) and `virtualdojo.__version__`
  (what `--version` prints) — and only the former was bumped. Both now agree, and a
  test fails the build if they ever drift again.

### v0.23.0 (2026-08-08)

**Bug Fixes:**
- `--format json` / `--format yaml` now emit plain, parseable output when stdout is
  not a terminal. Previously these were rendered through Rich, which hard-wrapped at
  the console width and injected newlines inside long string values, so
  `vdojo ... --format json | jq` failed with
  `Invalid string: control characters ... must be escaped`. Interactive output keeps
  its syntax highlighting; the `COLUMNS=100000` workaround is no longer needed.
- Errors and warnings now go to **stderr** instead of stdout. `vdojo ... 2>/dev/null`
  previously left the `✗ API Error (...)` text in the pipe, corrupting the data
  stream. Exit codes are unchanged.
- `records tree-export` no longer aborts when one child object can't be read. A child
  that returns 403 (or otherwise errors) is skipped with a warning and the export
  completes; previously a single inaccessible related object failed the whole run.

### v0.4.0 (2025-12-03)

**New Features:**
- File uploads now automatically generate AI embeddings by default
  - Uploaded files are processed for vector search and AI capabilities
  - Use `--no-embeddings` flag to skip AI processing for large binary files
  - Example: `vdojo files upload ./report.pdf` (with embeddings)
  - Example: `vdojo files upload ./large.zip --no-embeddings` (skip processing)

### v0.3.0 (2025-12-03)

**Bug Fixes:**
- Fixed `_in` and `_not_in` filter operators not handling multiple values correctly ([#1](https://github.com/Quote-ly/virtualdojo_cli/issues/1))
  - Now supports pipe delimiter: `name_in=VENDORS|DISTRIBUTORS|RESELLERS`
  - Now supports quoted commas: `name_in="VENDORS,DISTRIBUTORS,RESELLERS"`

**Improvements:**
- File downloads now use secure streaming endpoint (`/stream`) instead of presigned URLs
  - Downloads are authenticated on every request
  - No shareable URLs that could be leaked
  - Works correctly with MinIO in Docker environments

### v0.2.0 (2025-12-02)

- Initial public release
- Authentication with email/password
- Full CRUD operations on all CRM objects
- Schema discovery and exploration
- File management with progress tracking
- Multiple profile support
- Rich terminal output

## License

MIT License - see [LICENSE](LICENSE) file.

## Links

- [VirtualDojo CRM](https://virtualdojo.com)
- [API Documentation](https://docs.virtualdojo.com)
- [Issue Tracker](https://github.com/Quote-ly/virtualdojo_cli/issues)
