Command-line client for the SellerClaw Agent API — LLM-friendly JSON output, device-flow auth, auto-generated from OpenAPI.
Project description
sellerclaw-cli
Command-line client for the SellerClaw Agent API.
sellerclaw-cli is designed to be driven either directly from a terminal or, more commonly, as a subprocess by automation and LLM agents. Every command:
- returns structured JSON on stdout on success,
- returns structured JSON on stderr on failure,
- exits with a stable, categorical exit code,
- reads credentials only from env / config file — never from argv.
The CLI ships a hand-curated set of typed command groups over the SellerClaw Agent API, with built-in discovery (groups / commands / describe / guide) so an agent can explore the surface without external docs. When no curated command fits a Shopify task, a raw Admin GraphQL query/mutation is reachable via sellerclaw shopify graphql.
Table of contents
- Requirements
- Installation
- Quick start
- Configuration
- Authentication
- Using the CLI
- Output contract
- Using from scripts and LLM agents
- Agent identification
- Environment variables reference
- Troubleshooting
- Uninstall
- Development
Requirements
- Python 3.11+ (3.11, 3.12, 3.13 are all tested in CI).
- Linux, macOS, or Windows (POSIX file-permission handling applies only on Linux/macOS).
- Network access to your SellerClaw Agent API endpoint.
Installation
Install from PyPI:
pip install sellerclaw-cli
or with uv:
uv pip install sellerclaw-cli
# or, to install as a tool you can run anywhere:
uv tool install sellerclaw-cli
Verify the install:
sellerclaw --version
# 0.1.0
The installed binary is sellerclaw (not sellerclaw-cli).
Quick start
# 1. Install
pip install sellerclaw-cli
# 2. Authenticate (opens a URL + code you confirm in a browser)
sellerclaw auth login
# 3. Call an endpoint
sellerclaw shopify-listings list <STORE_ID> --status active --limit 5
That's it. See below for details on configuration, other auth flows, and output conventions.
Configuration
The CLI needs two pieces of configuration:
api_url— base URL of the Agent API (default:https://api.sellerclaw.com).token— your personalsca_…agent token used forAuthorization: Bearer …on every request.
Both can be supplied either via environment variables or via a config file.
Environment variables
| Variable | Purpose | Default |
|---|---|---|
SELLERCLAW_TOKEN |
Agent token (sca_…). Always sent as Authorization: Bearer <token>. |
(none) |
SELLERCLAW_API_URL |
Base URL of the Agent API. | https://api.sellerclaw.com |
XDG_CONFIG_HOME |
Base dir for the config file (standard XDG behavior). | ~/.config |
Example:
export SELLERCLAW_TOKEN="sca_abc123..."
export SELLERCLAW_API_URL="https://api.example.com"
sellerclaw auth whoami
Config file
The config file is a small TOML file stored at:
$XDG_CONFIG_HOME/sellerclaw/config.tomlifXDG_CONFIG_HOMEis set, otherwise~/.config/sellerclaw/config.toml.
Format:
api_url = "https://api.example.com"
token = "sca_abc123..."
Either key is optional. The file is created automatically by sellerclaw auth login / sellerclaw auth logout, with mode 0600 (owner read/write only) on POSIX systems.
You can also create or edit the file by hand:
mkdir -p ~/.config/sellerclaw
cat > ~/.config/sellerclaw/config.toml <<'EOF'
api_url = "https://api.example.com"
token = "sca_abc123..."
EOF
chmod 600 ~/.config/sellerclaw/config.toml
Priority / resolution order
For each of api_url and token, the CLI picks the first non-empty source, resolved independently:
- Environment variable (
SELLERCLAW_API_URL,SELLERCLAW_TOKEN). - Key in
config.toml(api_url,token). - Built-in default (
api_url = https://api.sellerclaw.com; no default token).
This means you can, for example, keep api_url in the config file and inject the token only via env — handy for CI and container workflows:
# ~/.config/sellerclaw/config.toml has only api_url
SELLERCLAW_TOKEN="$CI_TOKEN" sellerclaw shopify-listings list "$STORE_ID"
Inspecting the current config
sellerclaw auth whoami
# {"data":{"authenticated":true,"api_url":"https://api.example.com","config_path":"/home/you/.config/sellerclaw/config.toml"}}
authenticated: true means a token is configured (it is not validated against the server — make any real call to verify).
Authentication
Device flow (recommended)
Best for interactive use on your own machine:
sellerclaw auth login
The CLI prints a URL and a short code to stderr (so it never pollutes a stdout pipeline):
Open https://sellerclaw.com/activate
Enter the code: ABCD-1234
(waiting up to 600s, polling every 5s...)
Open the URL in a browser, paste the code, confirm. The CLI polls the API until the token is granted, then writes it to the config file. On success, stdout gets:
{"data":{"authenticated":true,"api_url":"https://api.sellerclaw.com","config_path":"/home/you/.config/sellerclaw/config.toml"}}
Email + password
sellerclaw auth login --password
# Email: you@example.com
# Password: ********
When stdin is not a TTY (e.g. piped), supply two lines — email, then password:
printf '%s\n%s\n' "you@example.com" "$PASSWORD" \
| sellerclaw auth login --password
Password is never echoed and never appears in argv or environment.
Manual token
If you already have an sca_… token (for example, provisioned by a backoffice tool), just put it into the config file or export the env var:
export SELLERCLAW_TOKEN="sca_abc123..."
# OR
echo 'token = "sca_abc123..."' >> ~/.config/sellerclaw/config.toml
Logging out
sellerclaw auth logout
Removes the token key from the config file. The api_url setting (and anything else in the file) is preserved.
Using the CLI
Command structure
sellerclaw [GLOBAL OPTIONS] <group> <command> [COMMAND ARGS]
Discover what's available:
sellerclaw --help # list top-level groups
sellerclaw <group> --help # list commands in a group
sellerclaw <group> <command> --help # show args + options for one command
Command groups
The CLI is organized into hand-curated top-level groups (channels, shopify-listings, ebay-listings, research-seo, orders, …), each with a small set of consistent verbs (list, get, create, update, delete, plus domain verbs like publish, sync, search). Path / parent ids are positional in path order; filters are flags:
# GET /agent/stores/{store_id}/listings?status=...&limit=...
sellerclaw shopify-listings list <STORE_ID> --status active --limit 10
# GET /agent/ebay/stores/{store_id}/listings/{listing_id}
sellerclaw ebay-listings get <STORE_ID> <LISTING_ID>
# POST /agent/research/seo/keyword-ideas
sellerclaw research-seo keyword-ideas --seed widgets
Use --help on any group or command to see its exact arguments.
Discovery
The discovery commands are driven by the live command registry (no external docs needed):
# One-shot onboarding: conventions, group list, how to call commands
sellerclaw guide
# Every group with a one-line summary and command count
sellerclaw groups
# Commands in a group (omit --group for all)
sellerclaw commands --group shopify-listings
# Full detail for one command: positionals, flags, body, example
sellerclaw describe shopify-listings create
When no curated command fits a Shopify task, run a raw Admin GraphQL operation with
sellerclaw shopify graphql <STORE_ID> -b '{"query": "...", "variables": {...}}'.
Passing request bodies
Any command that takes a request body accepts --body (short: -b; --json-body is kept as a deprecated alias). It supports three sources:
# 1. Literal JSON on the command line
sellerclaw shopify-listings publish <STORE_ID> \
--body '{"ids": ["l1", "l2"]}'
# 2. File
sellerclaw shopify-listings create <STORE_ID> \
--body @./product.json
# 3. Stdin (use @- as the value)
cat product.json | sellerclaw shopify-listings create <STORE_ID> \
--body @-
The body is validated as JSON locally before any network call; invalid JSON exits with a user_error (exit 1).
Output formats
--format is a global option (before the subcommand), defaulting to json:
| Value | Description |
|---|---|
json (default) |
Compact, single-line JSON. Ideal for pipelines (jq, LLM tools, scripts). |
pretty |
Indented JSON (2 spaces). Still valid JSON. |
yaml |
YAML. |
table |
ASCII table when the payload is a list of flat dicts; falls back to pretty JSON otherwise. |
sellerclaw --format pretty stores list-listings <STORE_ID>
sellerclaw --format yaml stores list-listings <STORE_ID>
sellerclaw --format table stores list-listings <STORE_ID>
Errors are always compact JSON on stderr, regardless of --format. Downstream error parsers can rely on a single stable shape.
Output contract
Success shape
On exit code 0, stdout contains exactly one JSON value (plus a trailing newline):
{"data": <payload>}
<payload> is whatever the API returned for that endpoint — a single object, a list, a primitive, or null.
Error shape
On any non-zero exit, stderr contains exactly one JSON value:
{
"error": {
"code": "auth_error",
"message": "Unauthorized",
"status": 401,
"details": { /* optional, parsed from the API response body */ },
"hint": "Run `sellerclaw auth login` to authenticate."
}
}
Field presence:
codeandmessage— always present.status— present when the error originated from an HTTP response.details— present when the API returned a structured error body.hint— present forauth_error(currently always points atsellerclaw auth login).
Exit codes
| Code | Meaning | Typical triggers |
|---|---|---|
0 |
Success | any 2xx |
1 |
User error | invalid CLI args, invalid JSON body, 4xx (non-auth), unknown group/command |
2 |
Server / network error | 5xx after retries, connection refused, timeout |
3 |
Auth error | 401, 403, missing token |
These are stable and categorical — scripts can switch on them without parsing stderr.
Retry behavior
The CLI transparently retries up to 3 attempts on these conditions:
- HTTP status
429,502,503,504 - Transient transport errors:
ConnectError,ReadError,WriteError, timeouts
Backoff between retries is exponential with a small jitter, capped at 10 seconds. When the server sends a Retry-After header (seconds), the CLI honors it exactly (no extra jitter on top). All other statuses and unknown httpx errors are returned immediately — no retries.
Requests time out after 30 seconds by default.
Using from scripts and LLM agents
The CLI was designed specifically to be wrapped as a subprocess. Conventions that make that robust:
- Pure JSON on stdout. No progress bars, no log lines, no
richdecoration leaking from stdout. Anything human-facing (login prompts, device-flow URL) is on stderr only. - Pure JSON on stderr on failure, with a stable shape.
- Categorical exit codes. You can branch on
returncodewithout touching stderr. - No credentials in argv. Even if you see a new CLI flag in the future, credentials will never be one of them. Pass them via env.
- No interactive prompts in non-TTY mode.
auth login --passwordreads from piped stdin; every other command just runs.
Minimal Python wrapper:
import json, os, subprocess
def run(*args, token=None, timeout=60):
env = {**os.environ}
if token:
env["SELLERCLAW_TOKEN"] = token
r = subprocess.run(
["sellerclaw", *args],
env=env,
capture_output=True,
text=True,
timeout=timeout,
)
if r.returncode == 0:
return json.loads(r.stdout)["data"]
err = json.loads(r.stderr)["error"]
if r.returncode == 3:
raise AuthError(err["message"]) # refresh token, retry
if r.returncode == 2:
raise TransientError(err["message"]) # safe to retry with backoff
raise UserError(err["message"], details=err.get("details"))
stores = run("stores", "list-listings", store_id, "--status", "active", token=my_token)
Minimal shell wrapper:
if out=$(sellerclaw shopify-listings list "$STORE_ID" --status active 2>err.json); then
echo "$out" | jq '.data'
else
code=$?
jq '.error' err.json
case $code in
3) echo "auth failed — refresh token" ;;
2) echo "transient — retry later" ;;
*) echo "fatal" ;;
esac
exit $code
fi
Agent identification
When the CLI is invoked from inside an LLM agent's workspace directory, it automatically attaches an X-Agent-Id header to every request so the server can attribute the call to the right agent. No flag, no env var, no caller-side wiring — the identifier is derived purely from the current working directory.
The CLI looks for a workspace-<id> segment in os.getcwd() and uses the last match. The id must match ^[A-Za-z0-9_-]+$ and be 1–64 characters; otherwise it is dropped silently and the header is not sent.
Example:
cwd /home/node/.openclaw/workspace-supervisor
→ X-Agent-Id: supervisor
cwd /home/node/.openclaw/workspace-product_scout/sub/dir
→ X-Agent-Id: product_scout
cwd /home/node (no workspace segment)
→ header omitted
The header is purely informational on the server side; requests without it work exactly the same. There is no override knob — by design, the id is whatever the cwd says it is.
Environment variables reference
| Variable | Read by | Purpose |
|---|---|---|
SELLERCLAW_TOKEN |
every command | Agent token, sent as Authorization: Bearer <token>. Highest priority. |
SELLERCLAW_API_URL |
every command | Base URL of the Agent API. Overrides config file and default. |
XDG_CONFIG_HOME |
auth *, whoami |
Base dir for the config file. Defaults to ~/.config. |
Troubleshooting
{"error":{"code":"auth_error",…,"status":401,…}} / exit 3
No token found, or token is wrong / expired. Run sellerclaw auth login, or check sellerclaw auth whoami to see which sources are in play and what api_url the CLI is targeting.
{"error":{"code":"network_error",…}} / exit 2
Can't reach the API. Verify SELLERCLAW_API_URL / config api_url, DNS, firewall. The CLI already retries transient failures 3× with backoff — a persistent network_error usually indicates a misconfigured URL or blocked egress.
{"error":{"code":"server_error",…,"status":5xx}} / exit 2
API itself returned 5xx after the CLI retried. If it persists, the platform is having an incident; otherwise retry later.
{"error":{"code":"user_error","message":"unknown group: …"}} / unknown command …
The group or command name doesn't exist in your installed version. Run sellerclaw groups, then sellerclaw commands --group <group> to see what's available; upgrade the package if the endpoint is newer.
{"error":{"code":"user_error",…}} on a missing positional argument
A required path / parent id wasn't supplied. Run sellerclaw describe <group> <command> to see the positionals (in path order) and an example invocation.
sellerclaw: command not found after pip install
The install directory isn't on PATH. On many systems pip install --user installs into ~/.local/bin; add that to PATH or use pipx install sellerclaw-cli / uv tool install sellerclaw-cli which handle it for you.
My config file keeps getting ignored
Check sellerclaw auth whoami — the config_path it prints is the one it reads. Common causes: XDG_CONFIG_HOME pointing somewhere unexpected, or a typo in the TOML (it must be api_url and token, lowercase, at the top level).
Uninstall
pip uninstall sellerclaw-cli
# optional — remove config + token
rm -rf ~/.config/sellerclaw
Development
This package lives in packages/sellerclaw-cli/ of the main SellerClaw monorepo. Command groups are hand-curated — see sellerclaw_cli/_command_group.py (declarative Cmd specs) and the modules under sellerclaw_cli/commands/. Discovery (groups / commands / describe / guide) is driven by the populated command registry, not an OpenAPI snapshot. When the API gains an endpoint, add or update the relevant group module.
From the repo root:
make cli-lint # ruff + pyright
make cli-test # unit tests (pytest + respx)
make cli-build # build wheel + sdist
Releases are triggered by pushing a cli-X.Y.Z tag.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sellerclaw_cli-0.23.0.tar.gz.
File metadata
- Download URL: sellerclaw_cli-0.23.0.tar.gz
- Upload date:
- Size: 33.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cea1da4c5e0227d63d1c264d3b48b310eabacede1a9fbe0e051a2b650bc387db
|
|
| MD5 |
a1eb5f5cf2a85e0efe6297bae80dc888
|
|
| BLAKE2b-256 |
e89d6d728f66cb6b4b176b27220b0fefbe1d691d69b8ebb121e29880cc1a67fc
|
Provenance
The following attestation bundles were made for sellerclaw_cli-0.23.0.tar.gz:
Publisher:
cli-release.yml on sellerai-com/sellerclaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sellerclaw_cli-0.23.0.tar.gz -
Subject digest:
cea1da4c5e0227d63d1c264d3b48b310eabacede1a9fbe0e051a2b650bc387db - Sigstore transparency entry: 1734073554
- Sigstore integration time:
-
Permalink:
sellerai-com/sellerclaw@a905fc0d235d0efd193cfa1200debe2e70e56184 -
Branch / Tag:
refs/tags/cli-0.23.0 - Owner: https://github.com/sellerai-com
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cli-release.yml@a905fc0d235d0efd193cfa1200debe2e70e56184 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sellerclaw_cli-0.23.0-py3-none-any.whl.
File metadata
- Download URL: sellerclaw_cli-0.23.0-py3-none-any.whl
- Upload date:
- Size: 53.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
947958c17ba104115bd481fa078ddb83141852735291d3e9b3589b9bfd234bc9
|
|
| MD5 |
264456b46cb6dabff50edfb28054ea19
|
|
| BLAKE2b-256 |
cc1b87ac20a0edf72fd78f3b481fe90c8c8892d6ec69f21816ba43ffdd706d60
|
Provenance
The following attestation bundles were made for sellerclaw_cli-0.23.0-py3-none-any.whl:
Publisher:
cli-release.yml on sellerai-com/sellerclaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sellerclaw_cli-0.23.0-py3-none-any.whl -
Subject digest:
947958c17ba104115bd481fa078ddb83141852735291d3e9b3589b9bfd234bc9 - Sigstore transparency entry: 1734073578
- Sigstore integration time:
-
Permalink:
sellerai-com/sellerclaw@a905fc0d235d0efd193cfa1200debe2e70e56184 -
Branch / Tag:
refs/tags/cli-0.23.0 - Owner: https://github.com/sellerai-com
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cli-release.yml@a905fc0d235d0efd193cfa1200debe2e70e56184 -
Trigger Event:
push
-
Statement type: