Metadata-Version: 2.4
Name: ninecli
Version: 0.1.6
Summary: Unofficial CLI for the Ninebot (九号) scooter Passport + business APIs
Author: ninecli contributors
License: MIT
Keywords: ninebot,scooter,ebike,cli
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Go
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# ninecli

Unofficial CLI for the Ninebot (九号) scooter app — read vehicle info,
view ride history, and run vehicle controls (engine start/stop, seat
trunk, find-me bell) from the terminal.

**Not affiliated with Ninebot / Segway.**

## Install

```bash
# one-off, no install
uvx ninecli

# or install into current env
pip install ninecli
```

Multi-platform wheels are published for Linux (amd64, arm64) and
macOS (amd64, arm64); `pip` / `uv` picks the right one automatically.

## Quick start

```bash
ninecli login -u <phone> -p <password>   # password login, saves tokens.json
ninecli whoami                           # verify the saved token
ninecli vehicles                         # list owned + shared vehicles
ninecli status                           # show one vehicle's status (asks which one)
ninecli travel                           # ride history for the current month
```

Vehicle controls (`bell`, `engine-start`, `engine-stop`, `buck`) print
a `y/N` safety prompt — pass `-y` to skip.

## Usage

```
$ ninecli --help
ninecli — Ninebot scooter Passport + vehicle info + control

Usage:
  ninecli [command]

Available Commands:
  bell         Ring the find-my-vehicle bell
  buck         ⚠️  Open the seat trunk
  completion   Generate the autocompletion script for the specified shell
  engine-start ⚠️  Power on / unlock the vehicle
  engine-stop  ⚠️  Power off / lock the vehicle
  help         Help about any command
  login        Password login (Passport) — saves tokens.json
  login-code   SMS-code login (Passport) — sends code, then consumes it
  mcp          Run a Model Context Protocol server for Ninebot APIs
  serve        Run an HTTP proxy exposing Ninebot APIs as plaintext REST
  status       Show vehicle status (location, battery, lock, acc, perms)
  travel       Ride history (default: list current month); --detail <id> shows one ride
  vehicles     List owned + shared vehicles
  whoami       Verify the saved token (calls POST /v5/user)

Flags:
      --config string          config directory (default: $NINEBOT_CONFIG_DIR or ~/.config/ninebot)
  -h, --help                   help for ninecli
      --json                   emit raw decrypted JSON instead of human-readable output
  -y, --yes                    bypass the y/N safety prompt on control commands (engine-start/engine-stop/buck)

Use "ninecli [command] --help" for more information about a command.
```

## REST proxy (`serve`)

```bash
ninecli serve                          # start local HTTP API
ninecli serve --token secret --quiet   # require Bearer auth + suppress logs
```

Serve exposes the encrypted Ninebot APIs as a plaintext REST server
driven by your saved `tokens.json`. It is useful for home-automation
integrations, dashboards, or scripts that would rather hit `curl` than
re-implement the Netease crypto layer.

Default bind: `127.0.0.1:18009`. Environment variables: `NINEBOT_SERVE_BIND`,
`NINEBOT_SERVE_TOKEN`.

Endpoints:

```text
GET    /healthz                       # always 200 (unauthenticated)
POST   /auth/login                    # {account,password}
POST   /auth/login-code               # {account} — send SMS code
POST   /auth/login-code/consume       # {account,code}
POST   /auth/refresh                  # rotate access_token
GET    /whoami
GET    /vehicles
GET    /vehicles/{sn}/status
GET    /vehicles/{sn}/travel?month=YYYYMM
GET    /vehicles/{sn}/travel/{detail_id}
POST   /vehicles/{sn}/engine/start
POST   /vehicles/{sn}/engine/stop
POST   /vehicles/{sn}/buck
POST   /vehicles/{sn}/bell
```

All responses use the envelope `{"ok": true, "data": ...}` or
`{"ok": false, "error": {"code": "...", "message": "..."}}`. Tokens
and credentials are **never echoed** in responses.

### Docker Compose deployment

Save the following as `docker-compose.yml`:

```yaml
services:
  ninecli:
    build:
      dockerfile_inline: |
        FROM ghcr.io/astral-sh/uv:python3.13-alpine
        RUN uv tool install ninecli
        ENV PATH="/root/.local/bin:${PATH}"
        ENTRYPOINT ["ninecli"]
    command: ["serve", "--bind", "0.0.0.0:18009"]
    ports:
      - "18009:18009"
    environment:
      NINEBOT_CONFIG_DIR: /config
      NINEBOT_SERVE_TOKEN: ${NINEBOT_SERVE_TOKEN:-}
    volumes:
      - ${NINEBOT_CONFIG_DIR:-~/.config/ninebot}:/config
    restart: unless-stopped
```

Usage:

```bash
# Optional: protect every non-/healthz endpoint with Bearer auth
export NINEBOT_SERVE_TOKEN=change-me

# Optional: override the host-side config directory mounted into /config
export NINEBOT_CONFIG_DIR=$HOME/.config/ninebot

docker compose up --build -d
curl http://127.0.0.1:18009/healthz
curl -H "Authorization: Bearer $NINEBOT_SERVE_TOKEN" \
  http://127.0.0.1:18009/vehicles
```

The compose service maps `18009:18009`, sets `NINEBOT_CONFIG_DIR=/config`
inside the container, and mounts `${NINEBOT_CONFIG_DIR:-~/.config/ninebot}`
from the host to `/config`. Run `ninecli login` on the host first, or use the
proxy auth endpoints (`POST /auth/login`, `POST /auth/login-code`) to create
`tokens.json` through the server.

## Model Context Protocol (`mcp`)

```bash
ninecli mcp                            # stdio MCP server (default)
ninecli mcp --http --bind 127.0.0.1:18019  # Streamable HTTP server
ninecli mcp --http --token secret      # require Bearer auth in HTTP mode
```

`mcp` runs a Model Context Protocol server that exposes Ninebot APIs as
typed MCP tools. AI clients (Claude, Cursor, etc.) can call vehicle read,
travel, control, and auth operations without re-implementing the Netease
crypto layer or managing tokens.

Default stdio transport is intended for local MCP clients. Use `--http` to
run a Streamable HTTP server. HTTP bearer auth is optional; when `--token` is
set, every request must include `Authorization: Bearer <token>`. Reuses the
same env variables as `serve`: `NINEBOT_SERVE_BIND`, `NINEBOT_SERVE_TOKEN`.

Tools exposed:

- Auth: `auth_login`, `auth_send_code`, `auth_consume_code`, `auth_refresh`
- Read: `whoami`, `vehicles`, `vehicle_status`, `travel`, `travel_detail`
- Control: `engine_start`, `engine_stop`, `buck`, `bell`

Control tools that change vehicle state (`engine_start`, `engine_stop`,
`buck`) include "requires user confirmation" in their descriptions so the
client knows to ask before calling.
