Skill: aixvcteam/axelrod (version 0.1.0)

--- name: axelrod description: AI-powered Base-chain trading and on-chain query agent via natural language. Use when the user wants to trade crypto (buy/sell/swap tokens), set up automated strategies (DCA, limit orders, RSI), check portfolio balances, view token prices, query token info/analysis, check order status, manage take-profit/stop-loss orders, or ask about crypto/DeFi topics on Base chain. Always run scripts/axelrod_chat.py to fetch real-time results. --- # Axelrod Execute Base-chain trading and on-chain queries using natural language through the AIxVC AI Automation. ## Quick Start ### First-Time Setup 1. **Configure credentials** in OpenClaw config under `skills.entries.axelrod.env`: ```json { "skills": { "entries": { "axelrod": { "enabled": true, "env": { "AIXVC_ACCESS_KEY": "your_access_key", "AIXVC_SECRET_KEY": "your_secret_key" } } } } } ``` 2. **Install dependencies**: ```bash pip install -r skills/axelrod/requirements.txt ``` 3. **Verify setup**: ```bash python skills/axelrod/scripts/axelrod_chat.py --message "check my balance" ``` ## Core Usage ### Simple Query For straightforward requests that complete quickly: ```bash python skills/axelrod/scripts/axelrod_chat.py --message "check my ETH balance" python skills/axelrod/scripts/axelrod_chat.py --message "what is ETH price now" ``` ### JSON Debug Mode For inspecting the full API response: ```bash python skills/axelrod/scripts/axelrod_chat.py --message "check my balance" --json ``` ## Mandatory Workflow 1. **Check AK/SK** — If `AIXVC_ACCESS_KEY` or `AIXVC_SECRET_KEY` is missing, ask the user to configure them. Do not guess or fabricate credentials. 2. **Run the script** — Whenever the request needs real-time on-chain data or execution, always run the CLI. Do not answer from model memory alone. 3. **Return stdout** — Capture the script's stdout and return it to the user. Light formatting is fine, but do not omit key results (amounts, tx hashes, confirm keys, error messages). 4. **Handle confirmation** — If the response includes `confirmKey`, guide the user to confirm or cancel (see Confirmation Flow below). ## Capabilities Overview ### Trading Operations - **Spot Buy/Sell/Swap**: Exchange tokens on Base chain - **DCA**: Dollar-cost averaging automation (e.g. "DCA 20u into ETH every day") - **Limit/Trigger Orders**: Execute at target prices or PnL rates - **RSI Strategy**: Conditional trading based on RSI indicators (e.g. "if 1h RSI < 30, buy 100u BTC") - **Take-Profit / Stop-Loss**: Automated risk management via `QUERY_ALGO` **Reference**: [references/api.md](references/api.md) ### Query Operations - **Balance Query**: Single token or all-asset portfolio - **Token Info & Analysis**: Contract details, decimals, analytics - **Price Query**: Real-time prices with 24h change - **Order Query**: Active order list and details - **TP/SL Order Query**: Take-profit/stop-loss order status ### Blockchain Assistant - Crypto/DeFi domain Q&A and execution guidance - Trading plan suggestions on Base chain ## Confirmation Flow Orders usually require risk-control confirmation; small orders (approximately ≤ $10) may skip confirmation. `confirmKey` is valid for about **10 minutes**. When the response includes `confirmKey`, ask the user to send one of: ```text yes, please execute <confirmKey> no, please cancel <confirmKey> ``` If the key has expired, the user must submit the original request again. ## Current Limitations | Limitation | Details | | ---------- | ------- | | Chain | Base only | | Multiple trades | One trade per message; ask user to split if needed | | Not supported | Leverage/futures/options, lending, cross-chain, NFT, fiat on/off-ramp | ## Common Patterns ### Check Before Trading ```bash # Check balance python skills/axelrod/scripts/axelrod_chat.py --message "check my ETH balance" # Check price python skills/axelrod/scripts/axelrod_chat.py --message "what is ETH price now" # Then trade python skills/axelrod/scripts/axelrod_chat.py --message "buy 50u of ETH" ``` ### Automated Strategies ```bash # DCA strategy python skills/axelrod/scripts/axelrod_chat.py --message "DCA 20u into ETH every day" # RSI conditional strategy python skills/axelrod/scripts/axelrod_chat.py --message "if 1h RSI < 30, buy 100u BTC" # Limit order python skills/axelrod/scripts/axelrod_chat.py --message "buy ETH when price drops to 2500" ``` ### Portfolio Review ```bash # Full portfolio python skills/axelrod/scripts/axelrod_chat.py --message "check my balance" # Token analysis python skills/axelrod/scripts/axelrod_chat.py --message "analyze AXR token for me" # Order status python skills/axelrod/scripts/axelrod_chat.py --message "show my recent orders" ``` ## Error Handling ### Exit Codes | Code | Meaning | Resolution | | ---- | ------- | ---------- | | `0` | Success | — | | `2` | Missing AK/SK | Ask user to configure `AIXVC_ACCESS_KEY` and `AIXVC_SECRET_KEY` | | `3` | HTTP failure or invalid/non-JSON response | Check network, verify endpoint is reachable | | `4` | API business error (`code` not in success set) | Read the error message, guide user to fix | ### Common Issues | Issue | Resolution | | ----- | ---------- | | "Please login first" | AK/SK is incorrect — reconfigure credentials | | Authentication error | Verify AK/SK are correct and not expired | | Insufficient balance | Reduce trade amount or add funds | | Token not found | Check token symbol or contract address | | confirmKey expired | Re-submit the original trade request | | Multiple trades in one message | Split into separate requests | ## Prompt Examples by Category ### Trading - `"buy 50u of AXR"` - `"sell 50% of my ETH"` - `"swap 10 SOL to ETH"` ### Automated Strategies - `"DCA 20u into ETH every day"` - `"if 1h RSI < 30, buy 100u BTC"` - `"buy ETH when price drops to 2500"` ### Portfolio & Queries - `"check my balance"` - `"show AXR token info"` - `"what is ETH price now"` - `"show my recent orders"` - `"show my TP/SL orders"` ### Blockchain Assistant - `"analyze AXR token for me"` - `"help me make a Base trading plan"` ## Best Practices ### Security 1. Never share your AK/SK credentials 2. Start with small test amounts 3. Verify token addresses before large trades 4. Review confirmation details carefully before executing ### Trading 1. Check balance before trades 2. Specify amounts clearly (`50u`, `50%`, `0.1 ETH`) 3. Start small, scale up after validation 4. Use limit orders for better entry prices 5. Set TP/SL for risk management ## API Reference For the full API contract, SigV4 signing protocol, request/response format, and response field details, see: **Reference**: [references/api.md](references/api.md) ## Implementation Notes - The script uses AK/SK with SigV4-style signing to call the AIxVC. - Current endpoint: `https://api.aixvc.io/gw/openapi/v2/public/twa/agent/chat` (`chain-id=base`). - If documentation conflicts with code behavior, follow the script implementation. ## File Structure - **SKILL.md** — Agent instructions (this file). The agent reads this to understand how to use the skill. - **README.md** — Human-facing setup and usage guide. - **scripts/axelrod_chat.py** — CLI client. Always invoke with `python skills/axelrod/scripts/axelrod_chat.py --message "<instruction>"`. - **references/api.md** — Full API contract and signing reference. - **requirements.txt** — Python dependencies (`requests`). ## Troubleshooting ### Script Not Working ```bash # Ensure Python 3 is available python --version # Install dependencies pip install -r skills/axelrod/requirements.txt # Test connectivity curl -I https://api.aixvc.io ``` ### API Errors See exit codes and common issues tables above. If an error persists: 1. Check the error message from stderr 2. Use `--json` mode to inspect the full API response 3. Verify AK/SK configuration 4. Test with a simple query first (`"check my balance"`) --- **💡 Pro Tip**: The most common issue is missing or incorrect AK/SK. Always verify credentials first when encountering errors. **⚠️ Security**: Keep your AK/SK private. Never commit credentials to version control. Only trade amounts you can afford to lose. **🚀 Quick Win**: Start by checking your balance to verify setup, then try a small trade like `"buy 1u of ETH"` to get familiar with the flow.

path: README.md
content: # Axelrod — Base Chain Trading Skill for OpenClaw

[AIxVC AI Automation](https://aixvc.io/chat) **skill pack** for [OpenClaw](https://github.com/openclaw/openclaw) (also known as Moltbot).

Axelrod enables every OpenClaw agent to trade, query, and automate strategies on the **Base chain** through the AIxVC AI Automation. It supports spot buy/sell/swap, DCA, limit/trigger orders, RSI strategies, token analysis, balance and price queries, order management, and take-profit/stop-loss orders — all via natural language commands.

This skill runs via a Python CLI at **scripts/axelrod_chat.py**, which calls the AIxVC AI Automation with AK/SK SigV4-style signing and returns human-readable results to the agent.

## Installation

1. Clone the axelrod repository:

   ```bash
   git clone https://github.com/AIxVC-Team/axelrod.git
   ```

2. **Add the skill directory** to OpenClaw config (`~/.openclaw/openclaw.json`):

   ```json
   {
     "skills": {
       "load": {
         "extraDirs": ["/path/to/axelrod"]
       }
     }
   }
   ```

   Use the path to the root of this skill (the skill lives at `SKILL.md`; the script is at `scripts/axelrod_chat.py`).

3. **Install dependencies** (required for the CLI):

   ```bash
   pip install -r skills/axelrod/requirements.txt
   ```

## Configure Credentials

**Configure credentials** under `skills.entries.axelrod.env`:

```json
{
  "skills": {
    "entries": {
      "axelrod": {
        "enabled": true,
        "env": {
          "AIXVC_ACCESS_KEY": "your_access_key",
          "AIXVC_SECRET_KEY": "your_secret_key"
        }
      }
    }
  }
}
```

| Variable             | Description                                        |
| -------------------- | -------------------------------------------------- |
| `AIXVC_ACCESS_KEY`   | AIxVC OpenAPI access key (AK).                     |
| `AIXVC_SECRET_KEY`   | AIxVC OpenAPI secret key (SK), used for signing.   |

To obtain the credentials:

1. Contact the AIxVC team or visit the AIxVC platform to register your agent.
2. Generate an AK/SK pair with AI Automation OpenAPI access enabled.
3. Save the keys to the OpenClaw config as shown above.

### Verify Setup

```bash
python skills/axelrod/scripts/axelrod_chat.py --message "check my balance"
```

> **⚠️ Note**: If the API returns "please login first", it means your AK/SK is incorrect. Please double-check and reconfigure your credentials.

## How It Works

- The skill exposes one entry point: **`axelrod`** via `SKILL.md`.
- The agent calls the CLI script **scripts/axelrod_chat.py** with a `--message` flag containing the user's natural language instruction.
- The script signs the request using AK/SK with AWS SigV4-style headers and sends it to the AIxVC AI Automation  OpenAPI gateway.
- The gateway returns a structured JSON response; the script extracts the user-facing reply and prints it to stdout.

**Tools** (via CLI):

| Command | Purpose |
| ------- | ------- |
| `--message "<instruction>"` | Send a natural language trading/query instruction to AIxVC |
| `--message "<instruction>" --json` | Same as above, but print the full JSON response for debugging |

## Capabilities Overview

### Trading Operations

- **Spot Buy/Sell/Swap**: Exchange tokens on Base chain
- **DCA**: Dollar-cost averaging automation
- **Limit/Trigger Orders**: Execute at target prices or PnL rates
- **RSI Strategy**: Conditional trading based on RSI indicators
- **Take-Profit / Stop-Loss**: Automated risk management orders (TP/SL)

### Query Operations

- **Balance Query**: Single token or all-asset portfolio
- **Token Info & Analysis**: Contract details, decimals, analytics
- **Price Query**: Real-time prices with 24h change
- **Order Query**: Active order list and details
- **TP/SL Order Query**: Take-profit/stop-loss order status

### Blockchain Assistant

- Crypto/DeFi domain Q&A and execution guidance
- Trading plan suggestions on Base chain

## Confirmation Flow

When a trade requires risk-control confirmation, the API response includes a `confirmKey`. The agent must guide the user to confirm or cancel:

- **Execute**: `yes, please execute <confirmKey>`
- **Cancel**: `no, please cancel <confirmKey>`

`confirmKey` is valid for approximately **10 minutes**. Small orders (≈ ≤ $10) may skip confirmation.

## Current Limitations

- **Chain**: Base only
- **Not supported**: Leverage/futures/options, lending, cross-chain bridge, NFT trading, fiat on/off-ramp
- Multiple independent trades in one message should be split into separate requests

## Prompt Examples

### Trading

- `"buy 50u of AXR"`
- `"sell 50% of my ETH"`
- `"swap 10 SOL to ETH"`
- `"DCA 20u into ETH every day"`
- `"if 1h RSI < 30, buy 100u BTC"`
- `"buy ETH when price drops to 2500"`

### Queries

- `"check my balance"`
- `"show AXR token info"`
- `"what is ETH price now"`
- `"show my recent orders"`
- `"show my TP/SL orders"`

### Assistant

- `"analyze AXR token for me"`
- `"help me make a Base trading plan"`

## API Reference

For the full API contract, signing protocol, and response format, see:

**Reference**: [references/api.md](references/api.md)

## Repository Structure

```
axelrod/
├── SKILL.md              # Skill instructions for the agent
├── README.md             # This file — setup and usage guide
├── requirements.txt      # Python dependencies
├── references/
│   └── api.md            # API contract and signing reference
└── scripts/
    └── axelrod_chat.py   # CLI client (AK/SK SigV4-signed)
```

---

**💡 Pro Tip**: Always specify token amounts clearly (e.g. `"buy 50u of AXR"` or `"sell 50% of my ETH"`) to avoid ambiguity.

**⚠️ Security**: Keep your AK/SK private. Never commit credentials to version control. Start with small test amounts.

**🚀 Quick Win**: Start by checking your balance (`"check my balance"`) to verify setup, then try a small trade on Base.


path: references/api.md
content: # Axelrod API Reference

Understanding the AIxVC TWA OpenAPI gateway for Base-chain trading and on-chain queries.

**Source**: `scripts/axelrod_chat.py` (canonical implementation)

## Core Pattern: Sign-Send-Parse

All Axelrod operations follow a synchronous pattern:

```
1. SIGN   → Build SigV4 headers with AK/SK
2. SEND   → POST JSON body to the gateway
3. PARSE  → Extract reply from response JSON
```

## API Endpoint

### POST /openapi/v2/public/twa/agent/chat

Send a natural language instruction to the AIxVC TWA agent.

**Request:**

```bash
curl -X POST "https://api.aixvc.io/gw/openapi/v2/public/twa/agent/chat" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "chain-id: base" \
  -H "X-Amz-Date: <UTC timestamp>" \
  -H "X-Amz-Content-Sha256: <payload sha256 hex>" \
  -H "[REDACTED_SECRET] Credential=..., SignedHeaders=host;x-amz-date, Signature=..." \
  -d '{"message": "buy 50u of AXR"}'
```

**Request Body:**

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `message` | string | Yes | Natural language instruction (e.g. "buy 50u of AXR", "check my balance") |

> **Note**: The request body is plain JSON — no envelope wrapper.

**Response (200 OK):**

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "reply": "You have 0.5 ETH on Base chain.",
    "intent": {
      "reply_to_user": "You have 0.5 ETH on Base chain."
    }
  }
}
```

## Required Environment Variables

| Variable | Description |
| -------- | ----------- |
| `AIXVC_ACCESS_KEY` | AIxVC OpenAPI access key (AK) |
| `AIXVC_SECRET_KEY` | AIxVC OpenAPI secret key (SK), used for SigV4 signing |

If either is missing, the CLI exits with code `2`.

## SigV4 Signing Protocol

The script signs every request with AWS SigV4-style headers using these fixed values:

| Parameter | Value |
| --------- | ----- |
| Region | `aixvc` |
| Service | `twa-manager` |
| Algorithm | `AWS4-HMAC-SHA256` |

### Canonical Request

```
POST
<canonical_uri>
<canonical_query (empty)>
host:<host>\nx-amz-date:<amz_date>\n
host;x-amz-date
<payload_sha256_hex>
```

| Field | Description |
| ----- | ----------- |
| Method | `POST` |
| Canonical URI | Request path, URI-encoded (slash preserved) |
| Canonical Query | Empty string |
| Signed Headers | `host;x-amz-date` |
| Payload Hash | `SHA256(body_bytes)` hex-encoded |

### Signing Key Derivation

```
kSecret  = "AWS4" + secret_key
kDate    = HMAC-SHA256(kSecret,  YYYYMMDD)
kRegion  = HMAC-SHA256(kDate,    "aixvc")
kService = HMAC-SHA256(kRegion,  "twa-manager")
kSigning = HMAC-SHA256(kService, "aws4_request")
```

### String to Sign

```
AWS4-HMAC-SHA256
<X-Amz-Date (YYYYMMDDTHHMMSSZ)>
<YYYYMMDD>/aixvc/twa-manager/aws4_request
SHA256(<canonical_request>)
```

### Final Signature

```
signature = HEX(HMAC-SHA256(kSigning, string_to_sign))
```

## HTTP Headers

| Header | Value |
| ------ | ----- |
| `Content-Type` | `application/json` |
| `Accept` | `application/json` |
| `chain-id` | `base` |
| `Content-Length` | `<len(body_bytes)>` |
| `X-Amz-Date` | `<UTC timestamp: YYYYMMDDTHHMMSSZ>` |
| `X-Amz-Content-Sha256` | `<payload sha256 hex>` |
| `Authorization` | `AWS4-HMAC-SHA256 Credential=<AK>/<scope>, SignedHeaders=host;x-amz-date, Signature=<sig>` |

## Response Contract

### Gateway Fields

| Field | Type | Description |
| ----- | ---- | ----------- |
| `code` | int/string | Business status code |
| `message` | string | Status message |
| `data` | object | Payload (see below) |

### Success Codes

The CLI accepts these values for `code` as success:
- `0`, `200`, `"0"`, `"200"`

Any other value is treated as a business error (exit code `4`).

### Reply Extraction

The user-facing reply is extracted from `data` in this priority order:

1. `data.reply` — Primary reply field
2. `data.intent.reply_to_user` — Fallback reply field
3. Compact JSON of `data` — Last resort if neither string field is present

## Confirmation Flow

When a trade requires risk-control confirmation, the response includes a `pendingConfirm` object:

```json
{
  "code": 0,
  "data": {
    "reply": "You are about to buy 50u of AXR...",
    "pendingConfirm": {
      "confirmKey": "abc123-def456"
    }
  }
}
```

| Field | Description |
| ----- | ----------- |
| `data.pendingConfirm.confirmKey` | Time-limited key (~10 min validity) |

Guide the user to send one of:

```text
yes, please execute <confirmKey>
no, please cancel <confirmKey>
```

If the key expires, the user must re-submit the original request.

## CLI Exit Codes

| Code | Meaning | Description |
| ---- | ------- | ----------- |
| `0` | Success | Request completed, reply printed to stdout |
| `2` | Missing AK/SK | `AIXVC_ACCESS_KEY` or `AIXVC_SECRET_KEY` not set |
| `3` | HTTP/parse failure | Network error, timeout, or non-JSON response |
| `4` | API business error | `code` not in success set (`0`/`200`) |

## Error Handling

### Authentication Errors

Missing or invalid AK/SK will result in exit code `2` or an HTTP-level error from the gateway.

> **Note**: If the API returns "please login first", it means the AK/SK is **incorrect**. This is not a session/login issue — reconfigure your credentials.

**Resolution**: Verify `AIXVC_ACCESS_KEY` and `AIXVC_SECRET_KEY` are correctly configured and not expired.

### Network Errors (Exit Code 3)

Connection failures, timeouts (default 60s), or non-JSON responses.

**Resolution**: Check network connectivity, verify the endpoint `https://api.aixvc.io` is reachable.

### Business Errors (Exit Code 4)

The gateway returned a response with a `code` not in the success set.

**Resolution**: Read the `message` field for details. Common causes:
- Invalid instruction format
- Unsupported operation
- Insufficient balance
- Token not found on Base chain

## Best Practices

### Request Construction
1. **Always use UTF-8** encoding for the body
2. **Match content length** precisely (`Content-Length` = byte length of body)
3. **Generate timestamps** in UTC immediately before signing
4. **Hash the body** exactly as sent (no whitespace modifications after hashing)

### Error Recovery
1. **Check exit code** first to categorize the error
2. **Use `--json` mode** to inspect the full response when debugging
3. **Retry on transient** network failures (code `3`)
4. **Read error messages** carefully for business errors (code `4`)

### Security
1. **Never log** the secret key
2. **Never hardcode** credentials in scripts
3. **Use environment variables** or secure config for AK/SK
4. **Rotate keys** periodically

---

**Remember**: The script implementation (`scripts/axelrod_chat.py`) is the canonical source of truth. If this document conflicts with the script behavior, follow the script.


path: requirements.txt
content: # Axelrod dependencies
requests>=2.25.0


path: scripts/axelrod_chat.py
content: #!/usr/bin/env python3
from __future__ import annotations

import argparse
import hashlib
import hmac
import json
import os
import sys
import time
from datetime import datetime, timezone
from urllib.parse import urlparse

try:
    import requests
except ModuleNotFoundError:
    requests = None


OPENAPI_ENDPOINT = "https://api.aixvc.io/gw"
CHAT_PATH = "/openapi/v2/public/twa/agent/chat"
HOST = "api.aixvc.io"
CHAIN_ID = "base"
AWS_REGION = "aixvc"
AWS_SERVICE = "twa-manager"
TIMEOUT_SECONDS = 60
EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"


def _eprint(*args: object) -> None:
    print(*args, file=sys.stderr)


def _json_compact(obj: object) -> str:
    return json.dumps(obj, separators=(",", ":"), ensure_ascii=False)


def _sha256_hex(raw: bytes) -> str:
    return hashlib.sha256(raw).hexdigest()


def _hmac_sha256(key: bytes, msg: str) -> bytes:
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()


def _uri_encode(raw: str, *, encode_slash: bool) -> str:
    out: list[str] = []
    for c in raw:
        if ("A" <= c <= "Z") or ("a" <= c <= "z") or ("0" <= c <= "9") or c in ("_", "-", "~", "."):
            out.append(c)
        elif c == "/" and not encode_slash:
            out.append(c)
        else:
            out.extend(f"%{b:02X}" for b in c.encode("utf-8"))
    return "".join(out)


def _derive_signing_key(secret_key: str, date_yyyymmdd: str, region: str, service: str) -> bytes:
    k_secret = f"AWS4{secret_key}".encode("utf-8")
    k_date = _hmac_sha256(k_secret, date_yyyymmdd)
    k_region = _hmac_sha256(k_date, region)
    k_service = _hmac_sha256(k_region, service)
    return _hmac_sha256(k_service, "aws4_request")


def _build_sigv4_headers(
    *,
    access_key: str,
    secret_key: str,
    url: str,
    body_bytes: bytes,
    chain_id: str,
    region: str,
    service: str,
) -> dict[str, str]:
    parsed = urlparse(url)
    amz_dt = datetime.now(timezone.utc)
    amz_date = amz_dt.strftime("%Y%m%dT%H%M%SZ")
    short_date = amz_dt.strftime("%Y%m%d")
    payload_hash = _sha256_hex(body_bytes) if body_bytes else EMPTY_SHA256
    canonical_uri = _uri_encode(parsed.path or "/", encode_slash=False)
    canonical_headers = f"host:{HOST}\nx-amz-date:{amz_date}\n"
    signed_headers = "host;x-amz-date"
    canonical_request = "\n".join(
        ["POST", canonical_uri, "", canonical_headers, signed_headers, payload_hash]
    )
    credential_scope = f"{short_date}/{region}/{service}/aws4_request"
    string_to_sign = "\n".join(
        ["AWS4-HMAC-SHA256", amz_date, credential_scope, _sha256_hex(canonical_request.encode("utf-8"))]
    )
    signing_key = _derive_signing_key(secret_key, short_date, region, service)
    signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
    authorization = (
        "AWS4-HMAC-SHA256 "
        f"Credential={access_key}/{credential_scope}, "
        f"SignedHeaders={signed_headers}, "
        f"Signature={signature}"
    )
    return {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "chain-id": chain_id,
        "Content-Length": str(len(body_bytes)),
        "X-Amz-Date": amz_date,
        "X-Amz-Content-Sha256": payload_hash,
        "Authorization": authorization,
    }


def _http_post_json(*, url: str, body_json: str, headers: dict[str, str]) -> tuple[int, str]:
    if requests is None:
        raise RuntimeError('missing dependency: requests. Install with "python -m pip install requests".')
    try:
        resp = requests.post(url, data=body_json, headers=headers, timeout=TIMEOUT_SECONDS)
    except requests.RequestException as exc:
        raise RuntimeError(f"request failed: {exc}") from exc
    resp.encoding = resp.encoding or "utf-8"
    return int(resp.status_code), resp.text


def _extract_reply(data: object) -> str:
    if isinstance(data, dict):
        reply = data.get("reply")
        if isinstance(reply, str) and reply.strip():
            result = reply.strip()
        else:
            intent = data.get("intent")
            if isinstance(intent, dict):
                r2u = intent.get("reply_to_user")
                if isinstance(r2u, str) and r2u.strip():
                    result = r2u.strip()
                else:
                    result = _json_compact(data)
            else:
                result = _json_compact(data)
 
        pending_confirm = data.get("pendingConfirm")
        if isinstance(pending_confirm, dict):
            confirm_key = pending_confirm.get("confirmKey")
            if confirm_key:
                result += f"\n\n**confirmKey**: `{confirm_key}`"
                result += f"\n- Confirm: `yes, please execute {confirm_key}`"
                result += f"\n- Cancel: `no, please cancel {confirm_key}`"
        return result
    if isinstance(data, str):
        return data.strip()
    if data is None:
        return ""
    return _json_compact(data)


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(
        prog="axelrod_chat.py",
        description="Axelrod (AIxVC) OpenAPI chat client (AK/SK signed).",
    )
    parser.add_argument("--message", required=True, help="Natural language instruction to send")
    parser.add_argument("--json", action="store_true", help="Print full JSON response")
    args = parser.parse_args(argv)

    access_key = os.environ.get("AIXVC_ACCESS_KEY", "").strip()
    secret_key = os.environ.get("AIXVC_SECRET_KEY", "").strip()
    if not access_key or not secret_key:
        _eprint("error: missing AK/SK. Set AIXVC_ACCESS_KEY and AIXVC_SECRET_KEY.")
        return 2

    url = f"{OPENAPI_ENDPOINT}{CHAT_PATH}"
    req_body = _json_compact({"message": args.message})
    req_body_bytes = req_body.encode("utf-8")
    req_headers = _build_sigv4_headers(
        access_key=access_key,
        secret_key=secret_key,
        url=url,
        body_bytes=req_body_bytes,
        chain_id=CHAIN_ID,
        region=AWS_REGION,
        service=AWS_SERVICE,
    )

    started = time.time()
    try:
        status, resp_body = _http_post_json(url=url, body_json=req_body, headers=req_headers)
    except RuntimeError as exc:
        _eprint(f"error: {exc}")
        return 3
    elapsed_ms = int((time.time() - started) * 1000)

    try:
        obj = json.loads(resp_body)
    except json.JSONDecodeError:
        _eprint(f"http error: status={status} cost={elapsed_ms}ms")
        if resp_body:
            _eprint(resp_body)
        return 3

    if args.json:
        print(json.dumps(obj, ensure_ascii=False, indent=2))

    if not isinstance(obj, dict):
        if not args.json:
            print(resp_body)
        return 0

    code = obj.get("code")
    if code not in (0, 200, "0", "200"):
        msg = obj.get("message") if isinstance(obj.get("message"), str) else "request failed"
        _eprint(f"api error: http={status} code={code} message={msg} cost={elapsed_ms}ms")
        if not args.json:
            _eprint(resp_body)
        return 4

    if not args.json:
        print(_extract_reply(obj.get("data")))

    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)