Metadata-Version: 2.4
Name: nexla-cli
Version: 0.2.0
Summary: Command-line client for the Nexla agent API.
Project-URL: Homepage, https://github.com/abhishekkumar705/nexla-agent-cli
Project-URL: Repository, https://github.com/abhishekkumar705/nexla-agent-cli
Project-URL: Issues, https://github.com/abhishekkumar705/nexla-agent-cli/issues
Author-email: Abhishek Kumar <abhishek.kumar@nexla.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Requires-Dist: httpx>=0.28.0
Requires-Dist: typer>=0.12.0
Provides-Extra: dev
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
Requires-Dist: pytest>=9.0.3; extra == 'dev'
Requires-Dist: respx>=0.23.1; extra == 'dev'
Requires-Dist: ruff>=0.15.12; extra == 'dev'
Description-Content-Type: text/markdown

# nexla-cli

Command-line client for the Nexla agent API. Depends on only `typer` and
`httpx` — no FastAPI, Daytona, Supabase, or other backend dependencies.

## Install

```bash
pipx install "git+https://github.com/abhishekkumar705/nexla-agent-cli.git"
```

Or with `uv`:

```bash
uv tool install "git+https://github.com/abhishekkumar705/nexla-agent-cli.git"
```

### From Node/TypeScript projects (no Python required)

```bash
npm install -g nexla-cli
# or run once-off:
npx nexla-cli sources list
```

This installs a prebuilt native binary (published to GitHub Releases by
`.github/workflows/release-binaries.yml`) behind a thin `npm/` wrapper — no
Python interpreter needed on the target machine. See `npm/README.md` for how
releases are cut.

### From PyPI

```bash
pip install nexla-cli
# or
uvx nexla-cli sources list
pipx install nexla-cli
```

Published by `.github/workflows/publish-pypi.yml` — see "Publishing to
PyPI" below.

## Publishing to PyPI

Tagging a release (`git tag vX.Y.Z && git push --tags` — same tag used to
trigger `.github/workflows/release-binaries.yml`, see `npm/README.md` for
the full release checklist) also triggers `publish-pypi.yml`, which builds
the sdist+wheel and publishes them to PyPI using
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC) —
no long-lived API token stored in this repo.

One-time setup required on pypi.org before the first release (repo owner
only, not automatable from here):

1. Create the `nexla-cli` project on PyPI (or reserve the name).
2. Project → Settings → Publishing → add a trusted publisher:
   - Owner: `abhishekkumar705`, repo: `nexla-agent-cli`
   - Workflow filename: `publish-pypi.yml`
   - Environment name: `pypi`
3. Nothing else to configure — no secrets to add. The workflow's
   `id-token: write` permission plus the `pypi` GitHub Environment satisfy
   PyPI's OIDC handshake automatically.

## Quick start

```bash
export NEXLA_API_URL=https://dev-api-express-code.nexla.com
export NEXLA_TOKEN=$(nexla login --service-key <your-service-key>)
nexla sources list
```

`nexla login` prints a bearer token to stdout (see command substitution
above); alternatively, set the following environment variables directly:

- `NEXLA_API_URL` — base URL of the deployed Nexla agent API
- `NEXLA_TOKEN` — bearer token to authenticate requests

## Using this CLI from Claude Code

The package ships a [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills) (`AGENTS.md` + `SKILL.md`, installed alongside the `nexla_cli` package) encoding invariants an agent can't infer from `--help` alone — output modes, `--dry-run`, `--json`/`--params` precedence, exit codes, and treating API responses as untrusted data.

Claude Code only discovers skills placed at `~/.claude/skills/<name>/SKILL.md` (global) or `.claude/skills/<name>/SKILL.md` (project-local) — a file merely present inside an installed pip package isn't picked up automatically. Symlink it in once, after installing the CLI. `pipx`/`uv tool install` both use an isolated venv, so a plain system `python3 -c "import nexla_cli"` won't find it — locate it inside that isolated venv instead:

```bash
mkdir -p ~/.claude/skills/nexla-cli

# if installed with `uv tool install`:
ln -sf "$(uv tool dir)/nexla-cli/lib/python3."*"/site-packages/nexla_cli/SKILL.md" \
  ~/.claude/skills/nexla-cli/SKILL.md

# if installed with `pipx install`:
ln -sf "$HOME/.local/pipx/venvs/nexla-cli/lib/python3."*"/site-packages/nexla_cli/SKILL.md" \
  ~/.claude/skills/nexla-cli/SKILL.md
```

A symlink (not a copy) means `nexla-cli` upgrades automatically pick up any future skill content updates. Restart Claude Code (or start a new session) after installing for it to be picked up.

## Global flags

Available on every command except `login` and `schema` (both always print raw output regardless of these flags):

| Flag | Effect |
|------|--------|
| `--output` / `-o` `table\|json\|ndjson` | Force an output mode. Defaults to `table` on a TTY, `json` otherwise (also settable via `NEXLA_OUTPUT`/`OUTPUT_FORMAT`). |
| `--fields id,name,...` | Mask output down to just these keys, on any `list`/`get`. |
| `--page-all` | Stream every page of a `list` command as NDJSON instead of returning one page. |

These can be placed before or after the subcommand, e.g. both `nexla --output json sources list` and `nexla sources list --output json` work.

Every mutating command also accepts `--dry-run`: validates the request body against the live API schema and prints `{"valid": ...}` without making the real (mutating) call.

## Raw JSON payloads

`create`/`update`-style commands (`sources`, `sinks`, `credentials`, `toolsets`, `nexsets transform`, `mcp-servers attach`, `tools set-runtime-config`) accept the full request body directly, not just their named flags:

```bash
nexla sources create --name my-source --connector s3 --json '{"credential_id": 123}'
nexla sources update 42 --params description="updated via params"
```

Precedence when a key is given more than one way: named CLI flags win, then `--json`, then `--params` (lowest). This lets you set a field the CLI hasn't added a dedicated flag for yet, without waiting on a CLI release.

## Response sanitization

Every response is passed through a sanitizer before rendering, in every output mode: ANSI escape sequences, control characters, and invisible Unicode (zero-width spaces, byte-order marks, bidirectional overrides) are stripped unconditionally — always on, no flag. This defends against a malicious API response field hijacking a human's terminal, or hiding text from a human while an agent still reads it. It is not a semantic filter — API response *content* should still be treated as untrusted data (see `AGENTS.md`), this only strips characters no legitimate field value would ever need.

## Full command reference

```bash
nexla --help
nexla <resource> --help
```

| Resource | Commands |
|----------|----------|
| `login` | `login --service-key <key> [--api-url <url>]` — exchanges a service key for a bearer token, printed to stdout |
| `schema` | `schema [<resource>.<verb>]` — machine-readable JSON signature of one command or the whole `/nexla/*` surface, fetched live from the deployed API's OpenAPI doc |
| `sources` | `list`, `get`, `create`, `update`, `activate`, `pause`, `delete`, `sample`, `file-upload` |
| `sinks` | `list`, `get`, `create`, `update`, `activate`, `pause`, `delete` |
| `nexsets` | `list`, `get`, `transform`, `activate` |
| `credentials` | `list`, `get`, `create`, `update`, `delete` |
| `flows` | `list`, `get`, `activate`, `pause`, `delete` |
| `transforms` | `test` |
| `connectors` | `search`, `describe`, `describe-credential`, `describe-credential-mode`, `describe-source`, `describe-source-endpoint`, `describe-sink`, `describe-sink-endpoint` |
| `probe` | `run` |
| `toolsets` | `list`, `get`, `create`, `update`, `delete`, `add-nexsets` |
| `tools` | `list`, `get`, `set-runtime-config`, `clear-runtime-config`, `delete` |
| `mcp-servers` | `list`, `attach`, `sync`, `detach` (nested under a toolset) |
| `context` | `get` |
| `orgs` | `list`, `get` |
| `code-containers` | `list` |
| `metrics` | `catalog`, `for-resource`, `get` |
| `users` | `list`, `get` |
| `notifications` | `list` |

`code-containers`, `metrics`, `users`, and `notifications` proxy resources the API hasn't implemented yet (they return HTTP 501 until it does).

## Exit codes

| Code | Meaning |
|------|---------|
| 0 | success |
| 2 | bad local input (validation, `--dry-run` failure) |
| 3 | `NEXLA_API_URL`/`NEXLA_TOKEN` not set |
| 4 | 401/403 from the API |
| 5 | 404 |
| 6 | 5xx from the API |
