Skip to content

Latest commit

 

History

History
422 lines (324 loc) · 9.46 KB

File metadata and controls

422 lines (324 loc) · 9.46 KB

Axis CLI API — Request/Response Examples

Complete, runnable examples for the most common operations. All HTTP examples assume axis is running with --api-port=3000.


Setup Examples

Example 1: Full Headless Setup (First Time)

Scenario: New VPS, no prior auth. Setting up axis to run fully headless.

# Step 1: Install
curl -fsSL https://github.com/bibliothecadao/eternum/releases/latest/download/install-axis.sh | bash

# Step 2: Configure LLM key
echo "ANTHROPIC_API_KEY=sk-ant-api03-..." >> ~/.eternum-agent/.env

# Step 3: Discover available worlds
axis worlds

Output of axis worlds:

Available worlds:
  my-world     (slot)     https://api.cartridge.gg/x/my-world/torii
  test-realm   (sepolia)  https://api.cartridge.gg/x/test-realm/torii
# Step 4: Authenticate with password (no browser needed)
axis auth my-world --method=password --username=myuser --password=mypassword

Output:

Authenticated successfully for world: my-world
Session stored at: ~/.eternum-agent/.cartridge/my-world/session.json
# Step 5: Start headless agent with HTTP API
axis run --headless --world=my-world --api-port=3000 --verbosity=decisions

Stdout (NDJSON):

{"type":"startup","timestamp":"2026-03-05T12:00:00Z","world":"my-world","version":"0.1.0"}
{"type":"session","timestamp":"2026-03-05T12:00:01Z","world":"my-world","active":true}
{"type":"heartbeat","timestamp":"2026-03-05T12:01:00Z","world":"my-world","tickCount":1}

Example 2: Check Auth Status Across All Worlds

axis auth --all --status --json

Output:

[
  {"world": "my-world", "status": "active", "expiresAt": "2026-03-06T12:00:00Z"},
  {"world": "test-realm", "status": "expired", "expiresAt": "2026-03-04T08:00:00Z"}
]

Action: For expired worlds, re-run auth before launching headless.


HTTP API Examples

Example 3: Queue a Prompt

Request:

curl -X POST http://127.0.0.1:3000/prompt \
  -H 'Content-Type: application/json' \
  -d '{"content":"Build a farm at your main realm"}'

Response (200 OK):

{"queued": true, "message": "Prompt queued for next tick"}

Example 4: Queue a Strategic Directive

Request:

curl -X POST http://127.0.0.1:3000/prompt \
  -H 'Content-Type: application/json' \
  -d '{"content":"Focus entirely on military expansion for the next 30 minutes. Build armies and attack weakly defended realms."}'

Response (200 OK):

{"queued": true, "message": "Prompt queued for next tick"}

What happens next: On the next tick (within tickIntervalMs ms), the agent will incorporate this directive into its decision-making and emit a decision event.


Example 5: Get Agent Status

Request:

curl -s http://127.0.0.1:3000/status

Response (200 OK):

{
  "world": "my-world",
  "loopEnabled": true,
  "tickIntervalMs": 60000,
  "sessionActive": true,
  "tickCount": 47,
  "lastTickAt": "2026-03-05T12:47:00Z",
  "modelProvider": "anthropic",
  "modelId": "claude-sonnet-4-5-20250929"
}

Example 6: Get World State Snapshot

Request:

curl -s http://127.0.0.1:3000/state

Response (200 OK):

{
  "world": "my-world",
  "observedAt": "2026-03-05T12:47:00Z",
  "realms": [
    {
      "id": "realm-001",
      "name": "Iron Fortress",
      "resources": {"food": 1500, "wood": 800, "stone": 300},
      "armies": 2
    }
  ],
  "pendingActions": [],
  "recentActions": [
    {"type": "build", "target": "farm", "realmId": "realm-001", "at": "2026-03-05T12:46:00Z"}
  ]
}

Example 7: Update Tick Interval (Speed Up Agent)

Request:

curl -X POST http://127.0.0.1:3000/config \
  -H 'Content-Type: application/json' \
  -d '{"changes":[{"path":"tickIntervalMs","value":30000}]}'

Response (200 OK):

{"applied": [{"path": "tickIntervalMs", "oldValue": 60000, "newValue": 30000}]}

Example 8: Pause the Agent Tick Loop

Request:

curl -X POST http://127.0.0.1:3000/config \
  -H 'Content-Type: application/json' \
  -d '{"changes":[{"path":"loopEnabled","value":false}]}'

Response (200 OK):

{"applied": [{"path": "loopEnabled", "oldValue": true, "newValue": false}]}

To resume:

curl -X POST http://127.0.0.1:3000/config \
  -H 'Content-Type: application/json' \
  -d '{"changes":[{"path":"loopEnabled","value":true}]}'

Example 9: Subscribe to SSE Event Stream

Request:

curl -N -H 'Accept: text/event-stream' http://127.0.0.1:3000/events

Response stream:

data: {"type":"heartbeat","timestamp":"2026-03-05T12:48:00Z","world":"my-world","tickCount":48}

data: {"type":"decision","timestamp":"2026-03-05T12:48:01Z","world":"my-world","decision":"Gathering resources this tick: sending caravans to iron mines"}

data: {"type":"action","timestamp":"2026-03-05T12:48:05Z","world":"my-world","action":{"type":"send_caravan","from":"realm-001","to":"mine-007","resources":{"capacity":100}}}

data: {"type":"heartbeat","timestamp":"2026-03-05T12:49:00Z","world":"my-world","tickCount":49}

Subscribe with verbosity filter (actions only):

curl -N -H 'Accept: text/event-stream' 'http://127.0.0.1:3000/events?verbosity=actions'

Example 10: Graceful Shutdown

Request:

curl -X POST http://127.0.0.1:3000/shutdown

Response (200 OK):

{"message": "Shutdown initiated"}

Final stdout events:

{"type":"shutdown","timestamp":"2026-03-05T13:00:00Z","world":"my-world","reason":"api_request"}

Stdin Control Examples

Example 11: Send Commands via Stdin Pipe

# Start axis with stdin enabled
axis run --headless --world=my-world --stdin &

# Send commands via stdin
echo '{"type":"prompt","content":"Scout the northern territories"}' | axis run --headless --world=my-world --stdin

# Or using a pipe in a loop
{
  echo '{"type":"prompt","content":"Gather resources"}'
  sleep 60
  echo '{"type":"config","changes":[{"path":"tickIntervalMs","value":45000}]}'
  sleep 300
  echo '{"type":"shutdown"}'
} | axis run --headless --world=my-world --stdin

Example 12: Orchestrator-Controlled Agent via Stdin

import subprocess
import json
import time

# Start axis with stdin pipe
proc = subprocess.Popen(
    ["axis", "run", "--headless", "--world=my-world", "--stdin"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

# Send a prompt
prompt_msg = {"type": "prompt", "content": "Focus on economic development"}
proc.stdin.write(json.dumps(prompt_msg) + "\n")
proc.stdin.flush()

# Read events from stdout
for line in proc.stdout:
    event = json.loads(line.strip())
    if event.get("type") == "action":
        print(f"Agent executed: {event['action']}")
    elif event.get("type") == "error":
        print(f"Error: {event['message']}")
        break

# Shutdown cleanly
proc.stdin.write(json.dumps({"type": "shutdown"}) + "\n")
proc.stdin.flush()
proc.wait()

Fleet Orchestration Example

Example 13: Run Multiple Worlds in Parallel

#!/bin/bash
# Pre-authenticate all worlds
axis auth --all --json > /tmp/auth-status.json

# Launch one agent per authenticated world
BASE_PORT=3000
PORT=$BASE_PORT

for world in $(jq -r '.[] | select(.status=="active") | .world' /tmp/auth-status.json); do
  echo "Starting agent for $world on port $PORT"
  axis run --headless \
    --world="$world" \
    --api-port=$PORT \
    --verbosity=actions \
    > /tmp/axis-${world}.log 2>&1 &

  echo "PID: $! World: $world Port: $PORT" >> /tmp/fleet-registry.txt
  PORT=$((PORT + 1))
done

echo "Fleet started. Registry at /tmp/fleet-registry.txt"

Send same prompt to all agents in fleet:

for port in $(seq 3000 $((3000 + $(wc -l < /tmp/fleet-registry.txt) - 1))); do
  curl -s -X POST "http://127.0.0.1:$port/prompt" \
    -H 'Content-Type: application/json' \
    -d '{"content":"Fortify defenses immediately"}' &
done
wait

Error Handling Examples

Example 14: Handling a 400 Bad Request

# Malformed request (missing content field)
curl -X POST http://127.0.0.1:3000/prompt \
  -H 'Content-Type: application/json' \
  -d '{"text":"this is wrong field name"}'

Response (400 Bad Request):

{"error": "Bad Request", "message": "Missing required field: content"}

Corrected request:

curl -X POST http://127.0.0.1:3000/prompt \
  -H 'Content-Type: application/json' \
  -d '{"content":"this is the correct field name"}'

Example 15: Handling Connection Refused

# This will fail if axis is not running
curl -s http://127.0.0.1:3000/status || echo "ERROR: axis not running on port 3000"

Shell output:

curl: (7) Failed to connect to 127.0.0.1 port 3000: Connection refused
ERROR: axis not running on port 3000

Resolution:

# Start axis with the API port
axis run --headless --world=my-world --api-port=3000 &
sleep 2  # Wait for HTTP server to initialize
curl -s http://127.0.0.1:3000/status

Example 16: Re-authenticate After Session Expiry

# Check status first
axis auth my-world --status

Output if expired:

World: my-world
Status: expired
Expired at: 2026-03-04T08:00:00Z
# Re-authenticate
axis auth my-world --method=password --username=myuser --password=mypassword

Output:

Authenticated successfully for world: my-world
Session stored at: ~/.eternum-agent/.cartridge/my-world/session.json
# Restart agent
axis run --headless --world=my-world --api-port=3000