CLI
Frank ships two CLI surfaces. They solve different problems and should both stay documented.
| Tool | Location | Audience | Purpose |
|---|---|---|---|
frankctl | frank-cli/ | Operators, developers, CI, agent runtimes | Operate the Frank API: auth, config, sources, streams, transforms, pipelines, datasets, schedules, runs, and AI workflows. |
frank | utils/frank_cli/ | Transform pattern authors | Scaffold, validate, test, and build custom transform pattern containers. |
Use frankctl to control the platform. Use frank when you are building reusable transform pattern packages.
frankctl
frankctl is the TypeScript, terminal-first API client.
cd frank-cli
npm install
npm run build
npm link
frankctl --helpFor local Compose, the API is usually exposed on 8002:
frankctl --api-url http://localhost:8002 status
frankctl config set profiles.default.apiUrl http://localhost:8002Global options
Available on every command:
| Flag | Purpose |
|---|---|
-p, --profile <name> | Select config profile. |
--json | Emit machine-readable JSON when the command supports table output. |
-v, --verbose | Verbose logging. |
-q, --quiet | Suppress non-error output. |
--no-color | Disable ANSI colors. |
--api-url <url> | Override the API base URL for this invocation. |
Environment variables
| Variable | Purpose |
|---|---|
FRANKCTL_API_URL | API base URL override. |
FRANKCTL_KEYCLOAK_URL | Keycloak base URL. |
FRANKCTL_KEYCLOAK_REALM | Keycloak realm. |
FRANKCTL_CLIENT_ID | OAuth client ID. |
FRANKCTL_CLIENT_SECRET | Service account client secret. |
FRANKCTL_PROFILE | Active config profile. |
FRANKCTL_TOKEN | Raw JWT override; bypasses token store. |
FRANK_DEV_MODE=true | Enables X-Tenant-ID forwarding for local development. |
FRANKCTL_TENANT_ID | Tenant ID forwarded when FRANK_DEV_MODE=true. |
PATTERN_WEBHOOK_SECRET | HMAC key for frankctl patterns register. |
Exit codes
| Code | Meaning |
|---|---|
0 | Success. |
1 | Generic error. |
2 | Usage error. |
3 | Authentication or authorization error. |
4 | API or service error. |
5 | Validation failed. |
6 | CLI config missing or invalid. |
Config and status
frankctl status
frankctl status --json
frankctl config set profiles.default.apiUrl http://localhost:8002
frankctl config get apiUrl
frankctl config listconfig get accepts effective profile fields:
apiUrl
keycloakUrl
realm
clientId
clientSecretConfig precedence is command flag, environment, config file, then built-in defaults.
Authentication
# Browser PKCE
frankctl auth login
# Headless password grant
frankctl auth login --headless --username [email protected] --password '...'
# Service account
frankctl auth login --service-account \
--client-id frank-api \
--client-secret "$FRANKCTL_CLIENT_SECRET"
frankctl auth status
frankctl auth token
frankctl auth logoutTokens are stored per profile. frankctl auth token prints a valid access token to stdout for use in scripts.
Sources
Manage source connections and source syncs.
frankctl sources list [--type <pattern-id>] [--status <s>] [--search <q>] [--page N] [--page-size N]
frankctl sources get <id> [--no-streams]
frankctl sources create -f source.yaml [--name <name>] [--type <pattern-id>]
frankctl sources update <id> -f patch.yaml
frankctl sources delete <id> --yesDiscovery:
frankctl sources discover <id> [--no-wait] [--timeout 300] [--poll-interval 2000]Sync:
frankctl sources sync <id> \
[--streams customers,orders] \
[--force-full-refresh] \
[--no-wait] \
[--timeout 1800] \
[--poll-interval 3000]Logs and history:
frankctl sources history <id> [--limit 20] [--offset 0] [--status <s>]
frankctl sources logs <id> <run-id> [-f] [--level <level>] [--poll-interval 2000]sources create and sources update read YAML, JSON, or - for stdin.
Source streams
frankctl sources streams list <source-id> [--enabled-only]
frankctl sources streams set <source-id> -f streams.yaml
frankctl sources streams refresh-schema <source-id> --from-discovery [--timeout 300] [--poll-interval 2000]
frankctl sources streams refresh-schema <source-id> -f discovery.jsonstreams set accepts either a bare stream array or the schema accepted by the CLI's bulk stream validator. refresh-schema either consumes a saved discovery payload or runs discovery first.
Transforms
frankctl transforms list [--status <s>] [--search <q>] [--pipeline-id <id>] [--page N] [--page-size N]
frankctl transforms get <id>
frankctl transforms runs <id> [--status <s>] [--limit 50] [--page N]
frankctl transforms logs <id> <run-id> [-f] [--level <level>] [--poll-interval 2000]
frankctl transforms trigger <id>Use transforms trigger for Run Now. It posts to:
POST /api/v1/transforms/{id}/schedule/triggerThat endpoint is the transform "Run Now" path. It dispatches through Dagster, even for manual schedules, and returns the Dagster run information.
Pipelines
frankctl pipelines list [--status <status>] [--search <q>] [--page N] [--page-size N]
frankctl pipelines get <id> [--include-version]
frankctl pipelines validate <id> [--sample-limit 1000] [--timeout 600] [--poll-interval 2500] [--no-wait]pipelines validate starts a sandbox run and polls until terminal status unless --no-wait is supplied. It always emits final JSON to stdout. Step progress is written to stderr.
Runs
frankctl runs get <workflow-id>
frankctl runs wait <workflow-id> [--timeout 600] [--poll-interval 2000]
frankctl runs cancel <id> --yesFor UUID IDs, runs cancel tries transform-run cancellation first and then source sync-run cancellation. For non-UUID IDs, it calls Temporal workflow cancellation.
Patterns
Pattern commands cover the platform's pattern catalog and external pattern lifecycle. Listing and detail commands browse source patterns; parameter validation checks transform pattern params; registration is used by CI publishing flows.
Commands:
frankctl patterns list [--category <c>] [--search <q>]
frankctl patterns get <id>
frankctl patterns validate-params <pattern-id> -f params.yaml
frankctl patterns register -f webhook-payload.jsonpatterns register is CI-oriented and requires PATTERN_WEBHOOK_SECRET. The CLI signs the payload with X-Hub-Signature-256.
Schedules
The frankctl schedules tree is source-scoped.
frankctl schedules list
frankctl schedules get <source-id>
frankctl schedules set <source-id> -f schedule.yaml
frankctl schedules pause <source-id>
frankctl schedules resume <source-id>
frankctl schedules trigger <source-id>
frankctl schedules delete <source-id> --yesExample:
schedule_type: cron
schedule_value: "0 */6 * * *"For transform execution, use frankctl transforms trigger <id>.
Datasets
frankctl datasets list [--layer bronze|silver|gold] [--namespace <ns>] [--page N] [--page-size N]
frankctl datasets preview <layer.namespace.table> [--limit 20]
frankctl datasets snapshots <layer.namespace.table>Dataset IDs use the dotted three-part form accepted by the FastAPI {dataset_id:path} route.
AI
All AI commands call Frank's Martha-backed AI routes and emit JSON.
frankctl ai compose-pipeline -f spec.yaml [--no-wait] [--timeout 180] [--poll-interval 2500]
frankctl ai generate-transform -f spec.yaml
frankctl ai review-sql -f spec.yaml
frankctl ai fix-ci-failure -f spec.yaml
frankctl ai suggest target-schema -f spec.yaml
frankctl ai suggest field-mappings -f spec.yaml
frankctl ai suggest pattern-params -f spec.yaml
frankctl ai publish-transform -f spec.yamlcompose-pipeline starts an execution and polls /api/v1/ai/compose-pipeline/{execution_id}/status unless --no-wait is set. If the backend returns no execution_id, the CLI treats it as validation failure and does not poll.
Python frank pattern CLI
The Python frank CLI is part of utils/ and installs from the frank-shared package.
cd utils
pip install -e ".[cli]"
frank --helpThis tool is for authoring custom transform patterns. It does not manage deployed sources, transforms, pipelines, or runs.
Scaffold a pattern
frank init <name> [--output patterns] [--template basic|sql|python]Examples:
frank init my-filter
frank init my-aggregator --template sql
frank init my-transform --output ./custom-patternsCreates:
pattern.yaml
main.py
requirements.txt
Dockerfile
tests/test_transform.py
tests/sample_config.json
README.mdValidate a pattern
frank validate [path] [--strict]Validation checks:
pattern.yamlexists.- Required schema fields are present.
- Field types match the pattern schema.
- Pattern ID format is valid.
params_schemais structurally sensible.- Container image hints are reasonable.
main.pyexists.
--strict fails on warnings as well as errors.
Test a pattern locally
frank test [path] [--config tests/sample_config.json] [--env-file .env.test]The test command loads a TRANSFORM_CONFIG-shaped JSON file, applies optional env vars, runs main.py, parses the emitted FrankResult, and can run Trino fixture setup/assertion/teardown blocks from the sample config.
Example config:
{
"artifact_id": "test-local-001",
"run_id": "test-run-001",
"tenant_id": "dev-tenant",
"source_table": "iceberg.bronze.orders",
"target_table": "iceberg.silver.orders_clean",
"params": {
"filter_expression": "status != 'deleted'"
}
}Build a pattern image
frank build [path] --tag <image-tag> [--push] [--registry ghcr.io/colivetree]Examples:
frank build patterns/my-filter --tag my-filter:v1
frank build . --tag ghcr.io/acme/my-filter:v1 --pushIf the pattern directory has no Dockerfile, the CLI generates one from pattern.yaml.
Which tool should I use?
| Task | Tool |
|---|---|
| Log in to Frank | frankctl auth login |
| Create or sync a source | frankctl sources ... |
| Configure source streams | frankctl sources streams ... |
| Trigger a transform run | frankctl transforms trigger <id> |
| Validate a pipeline sandbox | frankctl pipelines validate <id> |
| Preview an Iceberg dataset | frankctl datasets preview ... |
| Ask AI to compose or review | frankctl ai ... |
| Scaffold a custom transform pattern | frank init ... |
| Validate a local pattern package | frank validate ... |
| Run a pattern locally | frank test ... |
| Build and push a pattern image | frank build ... |