Metadata-Version: 2.4
Name: infilake
Version: 0.1.4
Summary: Agent-friendly CLI harness for infilake-dw data warehouse platform
Author: infilake-dw team
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: click>=8.1.0
Requires-Dist: prompt-toolkit>=3.0.0
Requires-Dist: python-dateutil>=2.9.0
Requires-Dist: requests>=2.31.0
Requires-Dist: sqlglot>=28.0.0
Requires-Dist: supabase>=2.28.0
Provides-Extra: mcp
Requires-Dist: mcp<2.0.0,>=1.27.0; extra == "mcp"

# infilake

Agent-friendly command line interface for the `infilake-dw` data warehouse
platform. The package installs two equivalent commands:

- `infilake`
- `cli-anything-infilake`

The CLI talks to the FastAPI backend, stores local configuration under the
current user's home directory, and supports both interactive terminal use and
machine-readable JSON output for agents and scripts.

## Requirements

- Python 3.11+
- A running `infilake-dw` backend
- Supabase URL and anon key for email/password login, or a warehouse API key

The default API endpoint is:

```bash
http://localhost:8000/api/v1
```

Use `--api-url` for a single command or `infilake config set --api-url ...` to
persist a different backend URL.

## Installation

Install the published package as a persistent tool from PyPI:

```bash
uv tool install 'infilake[mcp]'
```

For development, use the official `uv` CLI with the checked-in lockfile. The
project pins Python 3.12.10 in `.python-version`, requires uv-managed Python via
`uv.toml`, and creates its virtual environment in `cli/.venv`. An active Conda,
Anaconda, or other virtual environment is not used for the project.

```bash
cd cli
uv python install 3.12.10
uv sync --locked --all-extras
```

Run the development checkout through its project-local environment:

```bash
uv run --locked --all-extras infilake --help
uv run --locked --all-extras cli-anything-infilake --help
uv run --locked --all-extras infilake-mcp --help
```

Confirm which interpreter is in use:

```bash
uv run --locked --all-extras python -c 'import sys; print(sys.executable)'
```

The path must end in `cli/.venv/bin/python`, not an Anaconda or system Python
path. Only the `uv` executable is required on the host; Python and Python
packages are managed by uv.

## Quick Start

```bash
# Configure backend and Supabase auth
infilake config set \
  --api-url http://localhost:8000/api/v1 \
  --supabase-url "$SUPABASE_URL" \
  --supabase-anon-key "$SUPABASE_ANON_KEY"

# Log in with Supabase email/password
infilake auth login

# List warehouses and set a default target
infilake wh list
infilake wh use <name-or-uuid>

# Run SQL against the default warehouse
infilake q execute "SELECT 1 AS hello"

# Start the interactive REPL
infilake
```

API-key mode is also supported:

```bash
infilake auth use-key <warehouse-api-key> --name automation
infilake auth whoami
```

## Local State

The CLI writes state to `~/.cli-anything-infilake/`:

- `config.json`: non-secret configuration such as API URL, Supabase public
  anon key, default warehouse, pager, and TLS verification settings.
- `session.json`: JWT session or API key credentials. The file is written with
  user-only permissions where supported by the OS.

Useful commands:

```bash
infilake config show
infilake config set --api-url http://localhost:8000/api/v1
infilake config reset
infilake auth whoami
infilake auth logout
```

## Global Options

Global options must appear before the command group:

```bash
infilake [OPTIONS] COMMAND [ARGS]...
```

| Option | Description |
| --- | --- |
| `--json` | Print machine-readable JSON. |
| `--api-url URL` | Override the configured API URL for this invocation. |
| `-w, --warehouse NAME_OR_UUID` | Override the configured default warehouse. |
| `-v, --verbose` | Print HTTP method and URL to stderr. |
| `--no-color` | Disable colored output. |
| `--version` | Print CLI version. |

Examples:

```bash
infilake --json wh list
infilake -w analytics q execute "SELECT count(*) FROM public.events"
infilake --api-url https://api.example.com/api/v1 wh list
```

## Command Overview

| Group | Purpose |
| --- | --- |
| `auth` | Log in, log out, refresh JWTs, inspect identity, or switch to API-key mode. |
| `config` | Show, update, or reset local CLI configuration. |
| `wh` | Create, list, inspect, update, pause, resume, restore, delete, and test warehouses. |
| `q` | Execute SQL, dry-run SQL, cancel queries, and manage saved queries. |
| `tbl` | Manage schemas, tables, columns, snapshots, exports, imports, and table maintenance. |
| `udf` | Manage user-defined functions, versions, tests, logs, completions, and module whitelist settings. |
| `key` | Create, list, toggle, and delete warehouse-scoped API keys. |
| `engine` | Deploy, build, start, stop, remove, inspect, and test the query engine. |
| `pipe` | Import SQL pipelines, inspect DAGs, run pipelines or steps, manage triggers, and inspect runs. |
| `agent` | Install, list, diagnose, upgrade, and uninstall Claude Code, Codex CLI, or Pi integrations. |

Every group and subcommand has built-in help:

```bash
infilake wh --help
infilake q execute --help
infilake tbl create --help
infilake pipe trigger create --help
infilake agent install --help
```

## Agent Skills and MCP Integrations

The CLI packages four Infilake workflows and can install them together with a
secret-safe `infilake-mcp` registration. Preview changes before installation:

```bash
infilake agent install . --integration claude --dry-run
infilake agent install . --integration codex --integration-options='--skills' --dry-run
infilake agent install . --integration pi --dry-run
```

Install, inspect project runtime, diagnose, and remove an integration:

```bash
infilake agent install . --integration codex
infilake agent config .
infilake agent configure . --api-url https://infilake.example.com/api/v1
infilake agent list .
infilake agent doctor . --integration codex
infilake agent doctor . --integration codex --details
infilake agent uninstall . --integration codex --dry-run
infilake agent uninstall . --integration codex --yes
```

| Agent | Project workflows | Project MCP configuration | Additional prerequisite |
| --- | --- | --- | --- |
| Claude Code | `.claude/skills/<name>/SKILL.md` | `.mcp.json` | `claude` executable |
| Codex CLI | `.agents/skills/<name>/SKILL.md` | `.codex/config.toml` | `codex` executable |
| Pi Coding Agent | `.pi/prompts/<name>.md` | `.mcp.json`; optional `.pi/mcp.json` direct-tool override | `pi install npm:pi-mcp-adapter` |

Use `--components skills` or `--components mcp` for a partial installation,
and `--scope user` for user-level discovery paths. Pi uses the adapter's proxy
tool by default. A small allowlist can be exposed directly with, for example,
`--integration-options='--direct-tools=table_list,query_execute'`.

For a project-scope stdio MCP install, the interactive installer asks the user
to confirm the effective API URL and API key source before writing managed
files. A missing key is entered twice through hidden prompts; an existing key
is confirmed by source while its value remains hidden. Rejecting a user-session
or project-store key opens the same hidden, repeated input so a replacement can
be bound to the project. The endpoint is stored in
`.infilake/agent-runtime.json`; a key entered through the installer is stored
outside the repository under the current user's Infilake configuration
directory with owner-only permissions. Project files, Agent configuration,
command arguments, output, and the installation manifest never contain the key
value.

Runtime can also be configured independently of installation:

```bash
# Guided endpoint and hidden credential input
infilake agent configure .

# Import a credential without placing its value in argv
export PROJECT_INFILAKE_KEY='...'
infilake agent configure . \
  --api-url https://infilake.example.com/api/v1 \
  --credential-from-env PROJECT_INFILAKE_KEY

# Redacted human or machine-readable status
infilake agent config .
infilake --json agent config .
```

Environment variables remain the preferred ephemeral override for CI:

```bash
export INFILAKE_API_URL='http://localhost:8000/api/v1'
read -rsp 'Infilake API Key: ' INFILAKE_API_KEY
export INFILAKE_API_KEY
infilake agent install . --integration codex
```

API URL precedence is explicit option, environment, project runtime, user
configuration, then the non-sensitive default. API key precedence is explicit
legacy runtime input, the configured environment name, the project credential
store, then the current user API-key session. Stdio startup fails closed when
no key resolves.

Ordinary `infilake` commands launched by an Agent Skill use the same resolution
rules: the CLI searches from its working directory to the nearest ancestor
containing `.infilake/agent-runtime.json`. Run `agent configure` against the
same project directory where Codex is started; configuring another checkout or
subproject does not change the active project's runtime.

Non-interactive project stdio installation fails before mutation when the key
is missing. Use `--defer-credentials` only when managed files intentionally need
to be deployed first. `--dry-run` never prompts or persists credentials and
reports runtime readiness as a warning.

Install and doctor default to compact ordered stage nodes and a derived summary.
Use `--progress plain` for portable line output, `--progress off` to hide nodes,
and `--details` for every atomic finding. Global `--json` always disables live
progress and returns final `stages`, `summary`, `actions`, and `findings`.

### Codex MCP startup troubleshooting

If Codex reports that the `infilake` MCP server closed during `initialize`,
verify the SDK in the isolated uv tool environment and run the project-aware
diagnostic:

```bash
INFILAKE_TOOL_PYTHON="$(sed -n '1s/^#!//p' "$(command -v infilake-mcp)")"
"$INFILAKE_TOOL_PYTHON" -c \
  'from importlib.metadata import version; print(version("mcp"))'
infilake agent doctor . --integration codex --details
```

This release line requires MCP Python SDK `1.x`; SDK `2.x` does not provide the
`FastMCP` API used by the server. A correct SDK can still initialize only when
the configured Infilake API endpoint is reachable and the credential is valid.
Repair the project binding and refresh the project-scoped Codex registration
before restarting Codex:

```bash
infilake agent configure .
infilake agent install . \
  --integration codex \
  --integration-options='--skills'
```

## Warehouse Workflow

Create a warehouse:

```bash
infilake wh create \
  --name analytics \
  --s3-bucket infilake-data \
  --s3-access-key "$S3_ACCESS_KEY_ID" \
  --s3-secret "$S3_SECRET_ACCESS_KEY" \
  --s3-endpoint http://localhost:9000 \
  --s3-region us-east-1 \
  --s3-provider-type custom
```

Common warehouse commands:

```bash
infilake wh list
infilake wh get analytics
infilake wh use analytics
infilake wh test-connection analytics
infilake wh storage-get analytics
infilake wh quota
infilake wh pause analytics
infilake wh resume analytics
infilake wh delete-summary analytics
```

The default warehouse is resolved by this precedence:

1. Command-specific warehouse argument, when a command accepts one
2. Global `-w/--warehouse`
3. Saved default from `infilake wh use <name-or-uuid>`

## Query Workflow

Run inline SQL:

```bash
infilake q execute "SELECT * FROM public.events LIMIT 10"
```

Run SQL from a file:

```bash
infilake q execute --file query.sql
```

Pipe SQL through stdin:

```bash
printf 'SELECT 1 AS ok' | infilake q execute
```

Validate without executing:

```bash
infilake q dry-run --file query.sql
infilake q execute --dry-run "SELECT * FROM public.events"
```

Use saved queries:

```bash
infilake q saved create --name daily-count --file daily_count.sql --category ops
infilake q saved list --category ops
infilake q saved run <query-id> --limit 100
infilake q saved update <query-id> --favorite
```

## Table Workflow

Schemas:

```bash
infilake tbl schema list
infilake tbl schema create analytics
```

Create a table with column flags:

```bash
infilake tbl create analytics.events \
  --column id:uuid:pk \
  --column occurred_at:timestamp \
  --column payload:json \
  --comment "Application events"
```

Create a table from JSON:

```bash
infilake tbl create analytics.events --file table.json
```

Inspect and move data:

```bash
infilake tbl list
infilake tbl definition analytics.events
infilake tbl ddl analytics.events
infilake tbl data analytics.events --page 1 --page-size 50
infilake tbl export analytics.events --output events.csv --max-rows 1000
infilake tbl insert analytics.events --file row.json
```

Change table metadata and columns:

```bash
infilake tbl rename analytics.events --new-name app_events
infilake tbl comment analytics.app_events --comment "Application event stream"
infilake tbl column add analytics.app_events --name source --type text --nullable
infilake tbl column comment analytics.app_events source --comment "Event source"
infilake tbl column alter-type analytics.app_events source --type varchar
infilake tbl column drop analytics.app_events source
```

Snapshots and maintenance:

```bash
infilake tbl snapshot list analytics.app_events
infilake tbl snapshot diff analytics.app_events 42
infilake tbl maintenance expire-snapshots --older-than-days 30
infilake tbl maintenance cleanup-files --older-than-days 30
infilake tbl maintenance checkpoint
```

Maintenance commands default to dry-run where supported. Use `--execute` when
you intend to apply the operation.

## UDF Workflow

Create and manage UDFs:

```bash
infilake udf create \
  --name normalize_email \
  --file udf_normalize_email.py \
  --return-type text \
  --param email:text \
  --description "Normalize email addresses"

infilake udf list --status draft
infilake udf get <udf-id>
infilake udf enable <udf-id>
infilake udf versions <udf-id>
infilake udf logs <udf-id>
```

Test UDFs:

```bash
infilake udf test <udf-id> --mode manual --arg '"User@Example.COM"'
infilake udf test <udf-id> \
  --mode table_sample \
  --table analytics.users \
  --mapping email=email \
  --sample-rows 10
```

Manage the per-warehouse disabled module list:

```bash
infilake udf whitelist get
infilake udf whitelist set --disabled-modules os,subprocess
infilake udf whitelist set --disabled-modules ""
```

## API Keys

Warehouse-scoped API keys can be created by an authenticated user and then used
for non-interactive automation.

```bash
infilake key create \
  --name ci-runner \
  --role viewer \
  --allowed-schema analytics \
  --expires-at 2026-12-31T23:59:59Z

infilake key list
infilake key toggle <key-id> --inactive
infilake key delete <key-id>
```

The plaintext key is returned only once by `key create`. Save it immediately,
then use it with:

```bash
infilake auth use-key <warehouse-api-key> --name ci-runner
```

## Engine Workflow

Engine commands operate on the resolved warehouse:

```bash
infilake engine deploy
infilake engine build
infilake engine start
infilake engine status
infilake engine test-connection
infilake engine stop
infilake engine remove --yes
```

Use `-w/--warehouse` to target a specific warehouse for one command:

```bash
infilake -w analytics engine status
```

## Pipeline Workflow

Import and inspect a pipeline:

```bash
infilake pipe import-preview --file pipeline.yaml
infilake pipe import --file pipeline.yaml --project-id <project-id>
infilake pipe list
infilake pipe get <pipeline-id>
infilake pipe dag <pipeline-id>
```

Run a pipeline or one entry-point step:

```bash
infilake pipe run <pipeline-id> --param run_date=2026-05-23
infilake pipe step-trigger <pipeline-id> extract_events --param run_date=2026-05-23
```

Manage triggers and runs:

```bash
infilake pipe trigger create <pipeline-id> \
  --type cron \
  --scope pipeline \
  --cron "0 * * * *" \
  --timezone UTC

infilake pipe trigger list <pipeline-id>
infilake pipe runs <pipeline-id> --status RUNNING
infilake pipe run-get <run-id>
infilake pipe run-dag <run-id>
infilake pipe run-cancel <run-id>
```

## REPL

Run `infilake` with no subcommand to open the interactive REPL. The REPL reuses
the same command registry, so commands are entered without the leading
`infilake`:

```text
help
help q execute
? tbl create
wh list
q execute "SELECT 1"
tbl list
exit
```

## Output and Exit Behavior

- Human-friendly output is used by default.
- `--json` prints structured JSON for successful output and formatted errors.
- Destructive commands prompt for confirmation unless `--yes` is passed or
  JSON mode suppresses interactive prompts.
- JWT sessions are refreshed automatically when close to expiry.
- API-key mode sends `X-Api-Key`; JWT mode sends `Authorization: Bearer ...`.

## Project Structure

```text
cli/
|-- .python-version
|-- pyproject.toml
|-- README.md
|-- uv.lock
|-- uv.toml
`-- cli_anything/
    `-- infilake/
        |-- __main__.py
        |-- infilake_cli.py
        |-- commands/
        |   |-- agent.py
        |   |-- _ctx.py
        |   |-- engine.py
        |   |-- pipe.py
        |   `-- tbl.py
        |-- core/
        |   |-- api_keys.py
        |   |-- auth.py
        |   |-- client.py
        |   |-- config.py
        |   |-- engine.py
        |   |-- pipelines.py
        |   |-- queries.py
        |   |-- session.py
        |   |-- tables.py
        |   |-- udfs.py
        |   `-- warehouse.py
        |-- agent/
        |-- agent_assets/
        |-- tests/
        `-- utils/
```

## Development

Run CLI tests from the `cli` directory:

```bash
cd cli
uv sync --locked --all-extras
uv run --locked --all-extras pytest
```

Run a command from source:

```bash
cd cli
uv run --locked --all-extras infilake --help
```

The broader repository test commands are documented in the root `README.md` and
`AGENTS.md`.

## Publishing to a Python Package Registry

The distribution name is `infilake`. Publishing it makes the following install
command available from the selected registry:

```bash
uv tool install 'infilake[mcp]'
```

Changing `pyproject.toml` or building the package locally does not publish it.
Until at least one release has been uploaded to the configured registry, `uv`
reports that there are no versions of `infilake[mcp]`.

### 1. Prepare the release

Choose a new PEP 440 version and update the single canonical declaration in
`cli/cli_anything/infilake/__init__.py`: `VERSION`. Setuptools reads package
metadata from that attribute, and Agent assets reuse the same value. The
commands below derive `VERSION` from this source automatically. Do not add a
second version literal to `pyproject.toml` or `agent/assets.py`.

PyPI and TestPyPI do not allow an existing distribution filename or release
version to be overwritten, so every retry after a successful upload requires a
new version.

Run the CLI tests from the repository root:

```bash
cd cli
uv python install 3.12.10
uv sync --locked --all-extras
uv run --locked --all-extras pytest
```

### 2. Build and validate the artifacts

Twine is a locked development dependency in `cli/uv.lock`. Build both a source
distribution and wheel with uv into a version-specific directory. A
version-specific directory prevents an older artifact from being included
accidentally by a wildcard upload. Keep the following release commands in the
same shell so `INFILAKE_RELEASE_VERSION` remains available. `mkdir` is
deliberately used without `-p`: if that version directory already exists, stop
and inspect it instead of uploading possibly stale artifacts.

```bash
cd cli
INFILAKE_RELEASE_VERSION="$(uv run --locked python -c 'from cli_anything.infilake import VERSION; print(VERSION)')"
mkdir "dist/$INFILAKE_RELEASE_VERSION"
uv build --out-dir "dist/$INFILAKE_RELEASE_VERSION"
uv run --locked twine check "dist/$INFILAKE_RELEASE_VERSION"/*
```

The build must produce:

```text
cli/dist/<VERSION>/infilake-<VERSION>.tar.gz
cli/dist/<VERSION>/infilake-<VERSION>-py3-none-any.whl
```

Inspect the wheel before publishing and confirm that the `infilake-*.dist-info`
metadata, console entry points, and `agent_assets/skills/` files are present:

```bash
uv run --locked python -m zipfile -l \
  "dist/$INFILAKE_RELEASE_VERSION/infilake-$INFILAKE_RELEASE_VERSION-py3-none-any.whl"
```

Test the package directly from the checkout before relying on a registry:

```bash
uv run --locked --all-extras infilake --version
uv run --locked --all-extras infilake --help
uv run --locked --all-extras infilake-mcp --help
```

### 3. Test with TestPyPI

Create a TestPyPI account and API token, then upload the validated artifacts:

```bash
uv run --locked twine upload --repository testpypi \
  "dist/$INFILAKE_RELEASE_VERSION"/*
```

When prompted, use `__token__` as the username and the TestPyPI API token as
the password. Do not commit tokens to this repository or place them directly in
shell commands. TestPyPI is a separate registry and requires a separate account
and token from production PyPI.

Confirm that TestPyPI serves the exact release. `--no-deps` avoids trying to
resolve all runtime dependencies from the intentionally incomplete TestPyPI
index; `--target` also prevents the verification from modifying `cli/.venv`:

```bash
INFILAKE_TEST_TARGET="/tmp/infilake-testpypi-$INFILAKE_RELEASE_VERSION"
mkdir -p "$INFILAKE_TEST_TARGET"
uv pip install \
  --no-deps \
  --target "$INFILAKE_TEST_TARGET" \
  --default-index https://test.pypi.org/simple/ \
  "infilake==$INFILAKE_RELEASE_VERSION"
```

### 4. Publish to PyPI

After the tests and TestPyPI verification pass, upload the same artifacts to
production PyPI:

```bash
uv run --locked twine upload "dist/$INFILAKE_RELEASE_VERSION"/*
```

When prompted, use `__token__` as the username and the production PyPI API
token as the password. For automated releases, prefer PyPI Trusted Publishing
with short-lived OpenID Connect credentials instead of storing a long-lived API
token in CI.

Verify the published release with a clean `uv` tool environment:

```bash
uv tool install --force --refresh-package infilake \
  "infilake[mcp]==$INFILAKE_RELEASE_VERSION"
infilake --version
infilake-mcp --help
infilake agent --help
infilake agent install --help
```

The Agent help must include `configure` and `config`; install help must include
`--defer-credentials`, `--progress`, and `--details`. If they are absent, the
installed wheel is older than the release source even if its displayed version
matches.

If `uv` reports `there are no versions of infilake[mcp]`, check that the release
exists at `https://pypi.org/project/infilake/`, that the requested version was
uploaded, and that `uv` is using the expected registry. Immediately after an
upload, use `--refresh-package infilake` to bypass stale index metadata.

### Private registry

Upload to a private registry by supplying its upload endpoint:

```bash
uv run --locked twine upload \
  --repository-url https://registry.example.com/python/ \
  "dist/$INFILAKE_RELEASE_VERSION"/*
```

Install from the corresponding PEP 503-compatible `simple` index:

```bash
uv tool install \
  --default-index https://registry.example.com/python/simple/ \
  "infilake[mcp]==$INFILAKE_RELEASE_VERSION"
```
