Metadata-Version: 2.4
Name: chainwake
Version: 0.5.0
Summary: Bittensor and EVM chain monitoring for AI agents.
Project-URL: Homepage, https://github.com/taostat/chainwake
Project-URL: Documentation, https://github.com/taostat/chainwake/tree/main/docs
Project-URL: Repository, https://github.com/taostat/chainwake
Project-URL: Issues, https://github.com/taostat/chainwake/issues
Author-email: Blockmachine <hello@blockmachine.io>
License-Expression: MIT
License-File: LICENSE
Keywords: agent,base,bittensor,blockmachine,bnb-chain,bsc,chain,eip-1559,ethereum,mcp,substrate,taostat,taostats,watcher
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: <3.15,>=3.13
Requires-Dist: anyio==4.14.2
Requires-Dist: apprise==1.12.0
Requires-Dist: async-substrate-interface==2.2.1
Requires-Dist: cyclopts==4.22.2
Requires-Dist: httpx[http2]==0.28.1
Requires-Dist: mcp==1.28.1
Requires-Dist: pydantic==2.13.4
Requires-Dist: starlette==1.3.1
Requires-Dist: structlog==26.1.0
Requires-Dist: tenacity==9.1.4
Requires-Dist: uvicorn==0.51.0
Requires-Dist: websockets==16.1.1
Description-Content-Type: text/markdown

# Blockmachine Chainwake

<!-- mcp-name: io.github.taostat/chainwake -->

**Set a hook on chain state. Put your agent to sleep. Wake it the moment the
chain moves.**

Chain monitoring for AI agents across Bittensor, Ethereum, Base, and BNB Smart Chain.
Chainwake is a watcher CLI and MCP server. One invocation watches one condition —
a price threshold, a transaction reaching finality, an on-chain event — then
exits with a single structured JSON payload. It is observation-only: no keys,
no signing, no transaction submission.

![An agent sets a chainwake hook on subnet 19's price, sleeps at zero token cost, then wakes when the price breaks 0.01 TAO, fetches TAO's USD price, and sends a Telegram report](https://raw.githubusercontent.com/taostat/chainwake/main/docs/assets/demo.gif)

## Why

An agent that needs to react to chain state has two bad options today:

- **Poll in a loop.** Every empty check burns model tokens and context, and
  the agent still reacts late.
- **Stay out of it.** A human watches a dashboard and re-prompts the agent.

Chainwake is the third option. The agent starts a watcher and **ends its
turn**. No model runs while the watcher waits — the hook lives in a cheap
subprocess holding a WebSocket subscription. When the condition fires, the
watcher exits with JSON on stdout and the host wakes the agent with the
result. Tokens are spent only on the turns where something actually happened.

```
agent turn ──▶ chainwake watcher (no tokens) ──▶ condition fires ──▶ agent woken with JSON
```

## What you can watch

| Chain | Examples |
|-------|----------|
| `bt` (Bittensor) | TAO/USD and subnet prices, validator/neuron state, account balance and transfers, hyperparameter changes, chain events, epoch boundaries |
| `eth` (Ethereum) | Transaction receipt, confirmations, finality; EIP-1559 base fee; ERC-20 USD prices |
| `base` (Base) | Transaction finality (`safe`/`finalized`), L2 and L1 fee inputs; ERC-20 USD prices |
| `bsc` (BNB Smart Chain) | Transaction confirmations and finality, gas price, BEP-20 USD prices |

The full observable catalogue is in the
[CLI reference](https://github.com/taostat/chainwake/blob/main/docs/cli-reference.md).

## Install

```sh
uv tool install chainwake        # isolated install (recommended)
pip install chainwake            # or into the active environment
```

To try unreleased code: `uv tool install git+https://github.com/taostat/chainwake.git`.
Enable shell completion with `chainwake --install-completion`.

## Quickstart

Every command below blocks until its condition fires, then exits with one
JSON payload. All defaults are anonymous public endpoints — no configuration,
no API key needed for a first watcher.

Wait for a Bittensor subnet price to cross a threshold, or move 5% within an
hour:

```sh
chainwake bt subnet 19 price --below 0.05
chainwake bt subnet 19 price --drop-pct 5 --window-time 1h
```

Watch TAO/USD directly:

```sh
chainwake bt network tao-price --below 180
chainwake bt network tao-price --move-pct 5 --window-time 1h
```

TAO/USD is a CoinGecko aggregate quote sampled every 60 seconds. It supports
thresholds and percentage moves with a time window or watcher-start baseline.

The window controls which samples are compared; it does not impose a runtime limit.
Omit it to keep the first successful observation as an unexpiring baseline.
`--max-runtime` alone controls when an unmatched watcher exits with `timeout`.

Wait for an on-chain event:

```sh
chainwake bt event --type subnet-registered --max-runtime 24h
```

Wait for an Ethereum transaction, or for the EIP-1559 base fee (in gwei) to
get cheap:

```sh
chainwake eth tx 0x0123...abcd --finality finalized
chainwake eth network base-fee --below 10
```

Base and BSC use the same grammar with chain-specific finality and fees:

```sh
chainwake base tx 0x0123...abcd --finality safe
chainwake base network l1-blob-base-fee --below 2
chainwake bsc tx 0x0123...abcd --confirmations 12
```

Watch DAI by symbol on any supported EVM chain, or GRAM on Ethereum/BSC:

```sh
chainwake eth token DAI price --below 0.995
chainwake base token DAI price --above 1.005
chainwake bsc token GRAM price --rise-pct 10 --window-time 1h
```

Token symbols are resolved within the selected chain. If a symbol is ambiguous,
pass its contract address instead. Prices are CoinGecko aggregate USD quotes,
sampled every 60 seconds by default; set `CHAINWAKE_COINGECKO_API_KEY` only if
anonymous requests hit CoinGecko's rate limit.

Fee and transaction watchers subscribe to `newHeads` and pin reads to the
notified block.
Transaction watches report `success` or `reverted`, gas used, and effective
gas price; a missing receipt stays pending — Chainwake never guesses that a
transaction was dropped or replaced.

### The payload

A matched watcher exits `0` with stdout:

```json
{
  "status": "matched",
  "watcher": { "chain": "bt", "resource": "subnet", "resource_id": "19",
               "sub_resource": "pool.price", "name": null,
               "primitive": "threshold",
               "invocation": ["chainwake", "bt", "subnet", "19", "price",
                              "--below", "0.05"] },
  "condition": { "operator": "below", "target": 0.05 },
  "observed": { "path": "subnet.19.pool.price", "value": 0.0487,
                "block": 4291820, "block_hash": "0xabc...",
                "timestamp": "2026-05-06T10:00:00Z" },
  "budget": { "runtime_ms": 3210, "rpc_calls": 3, "estimated_ru_consumed": 3 },
  "process": { "pid": 12345, "started_at": "2026-05-06T09:59:57Z" }
}
```

The payload contract is
[`schemas/output.json`](https://github.com/taostat/chainwake/blob/main/schemas/output.json)
— the sole current output contract, regenerated from the Pydantic models and
CI-checked. Consumers should validate against it and reject unknown fields.

## Waking your agent

### MCP (Hermes, OpenClaw, Claude Desktop, Cursor)

The built-in MCP server exposes every watcher as an MCP tool:

```sh
chainwake mcp serve --stdio
chainwake mcp config hermes
chainwake mcp config openclaw
```

Hermes users install `taostat/chainwake` from the dashboard's Plugins page;
OpenClaw users run `openclaw skills install @blockmachine/chainwake --global`.
Both use native background-process completion notifications — no polling, no
long-held MCP request. See the
[MCP guide](https://github.com/taostat/chainwake/blob/main/docs/mcp.md).

### Subprocess

Any agent framework that can spawn a process can set a hook:

```python
import json, subprocess

proc = subprocess.run(
    ["chainwake", "bt", "subnet", "19", "price",
     "--below", "0.05", "--max-runtime", "5m", "--json"],
    capture_output=True, text=True,
)
payload = json.loads(proc.stdout)
if proc.returncode == 0:
    print(f"Matched at block {payload['observed']['block']}")
elif proc.returncode == 1:
    print(f"No match: {payload['reason']}")  # timeout or budget_exhausted
```

See the
[agent integration guide](https://github.com/taostat/chainwake/blob/main/docs/agent-integration.md)
for parallel wakes, detached processes, and restart durability.

### Durable jobs

Add `--durable` when the watcher must outlive the shell or agent turn that
created it. Chainwake persists the watcher, starts a local supervisor, prints
the job id, and exits immediately:

```sh
chainwake --json --durable \
  --context "Tell me the observed price and block." \
  bt subnet 19 price --below 0.05

chainwake --json jobs wait <job-id>   # blocks without polling the chain
```

The completion carries your `context` plus the normal watcher result. Manage
jobs with `jobs list`, `jobs show`, `jobs cancel`. Because job arguments are
stored locally, durable mode rejects literal `--api-key`/`--rpc-url` values —
use `CHAINWAKE_BT_API_KEY` / `CHAINWAKE_BT_RPC_URL` in the supervisor
environment.

### Notifications (`--out`)

Route results anywhere apprise can deliver (~100 destinations: Telegram,
Discord, Slack, email, webhooks):

```sh
chainwake bt subnet 19 price --below 0.05 --out "tgram://bottoken/chatid"
chainwake bt subnet 19 price --below 0.05 --out stream            # NDJSON, keep running
chainwake bt subnet 19 price --below 0.05 --out file:///tmp/w.ndjson
```

`--out` is repeatable. See the
[apprise URI reference](https://github.com/caronc/apprise/wiki).

## Exit codes

Agents parse JSON for detail; exit codes drive shell-level control flow.

| Code | `status` field | Meaning |
|------|----------------|---------|
| `0`  | `matched` | Condition fired |
| `1`  | `stopped` / `timeout` / `budget_exhausted` | Finished without a match |
| `2`  | `user_error` | Invalid args, unknown resource |
| `3`  | `provider_error` / `auth_error` | RPC unavailable or credentials required |
| `4`  | `internal_error` | Bug in chainwake |

For a watcher invocation, automation should always pass `--json`; every
watcher exit then emits one JSON envelope. Piped stdout also selects JSON, but
do not depend on TTY detection. Without `--json`, an interactive TTY uses
human-readable output. Help, version, and MCP configuration helpers are
outside the watcher-envelope contract. Stderr carries diagnostics and is not
part of the stable contract.

## Configuration

| Method | Precedence | Example |
|--------|-----------|---------|
| `--rpc-url` flag | Highest | `--rpc-url wss://my-node:9944` |
| Env var | Middle | `CHAINWAKE_BT_RPC_URL`, `CHAINWAKE_ETH_RPC_URL` |
| Anonymous default | Lowest | `wss://rpc.blockmachine.io` (bt), `wss://ethereum-rpc.publicnode.com` (eth) |

[Blockmachine](https://blockmachine.io) is the default Bittensor RPC provider;
the anonymous free tier needs no API key. Any Substrate-compatible WebSocket
endpoint works. No API key is required for a first watcher. Pass `--api-key`
(or `CHAINWAKE_BT_API_KEY`) for higher-limit access. Base and BSC default to
`wss://base-rpc.publicnode.com` and
`wss://bsc-rpc.publicnode.com`.

`--max-runtime` accepts `30s`, `10m`, `2h`, `1d`. Default is unbounded.
Agent and automation calls should always set a bounded runtime.
`--max-ru` and `budget.estimated_ru_consumed` form a registry-estimated
observation budget, not a provider billing cap.

## Documentation

- [README](https://github.com/taostat/chainwake/blob/main/README.md)
- [Quickstart](https://github.com/taostat/chainwake/blob/main/docs/quickstart.md)
- [Concepts](https://github.com/taostat/chainwake/blob/main/docs/concepts.md) — primitives, registry, polling model
- [CLI reference](https://github.com/taostat/chainwake/blob/main/docs/cli-reference.md)
- [Agent integration](https://github.com/taostat/chainwake/blob/main/docs/agent-integration.md)
- [MCP guide](https://github.com/taostat/chainwake/blob/main/docs/mcp.md)
- [Notification adapters](https://github.com/taostat/chainwake/blob/main/docs/adapters.md)
- [JSON output schema](https://github.com/taostat/chainwake/blob/main/schemas/output.json)
- [Historical design proposal (archived)](https://github.com/taostat/chainwake/blob/main/spec.md)

## Status

Chainwake is in active development. See
[`PROGRESS.md`](https://github.com/taostat/chainwake/blob/main/PROGRESS.md)
for in-flight work. Issues and ideas:
[GitHub Issues](https://github.com/taostat/chainwake/issues).

## License

MIT
