1 Automation and AI
uncaney edited this page 2026-08-14 14:00:34 +02:00

Automation and AI

Pandapi is built to be driven by programs: shell scripts, CI jobs, daemons, and LLM agents. This page collects the patterns. Command details: Command-Reference and docs/API.md.

Why this API scripts well

  • One JSON object per line, over a socket or the bundled CLI: trivially consumable from any language, jq-friendly.
  • Self-describing: api.describe returns all 110 commands with one-line descriptions, the 19 event types, and the machine-readable security model.
  • Structured errors {code, message, retryable}: retry loops branch on a stable code, not message text.
  • Blocking conveniences: instances.start {wait} and ops.wait remove polling boilerplate.
  • Replayable events: monotonic seq + a 4096-event ring means a supervisor that restarts loses nothing recent.
  • Headless mode: the whole thing runs GUI-less on a server (see Quickstart).

Shell patterns

# Retry-on-retryable wrapper
call() {
  for i in 1 2 3; do
    out=$(ekaii-launcher --api "$1" --api-params "${2:-{}}") && { echo "$out"; return 0; }
    echo "$out" | jq -e '.error.retryable' >/dev/null || { echo "$out" >&2; return 1; }
    sleep 2
  done
  return 1
}

# Provision, launch, watch, tear down
call instances.ensure '{"name":"ci","version":"26.2","loader":"fabric"}'
op=$(call content.install '{"name":"ci","files":[{"modrinth":{"project_id":"AANobbMI"}}]}' | jq '.data.op')
call ops.wait "{\"op\":$op,\"timeout_secs\":600}"
call instances.start '{"name":"ci","wait":"running","timeout_secs":600}'
call logs.tail '{"name":"ci","min_level":"ERROR","tail":50}'
call instances.kill '{"name":"ci"}'

For a Python client with id correlation, see Quickstart.

Idempotent provisioning and IaC

  • instances.ensure: create-if-absent plus apply configuration, in one idempotent call. The building block for declarative setups.
  • content.sync: reconcile an instance's mods folder to a desired set, keyed by provenance (Modrinth/CurseForge project ids). Manual mods are left alone; remove_extra prunes managed strays.
  • state.export: a revisioned snapshot of the whole launcher (per-instance SHA-1 revision, folded into a global_revision). Compare revisions across runs for cheap drift detection, and archive the document as backup.
  • launcher.plan: diff a desired-state document against reality and get back a replayable list of API calls (create/modify/delete actions); it changes nothing itself. Review, then replay.
  • instances.manifest: a content-hashed manifest of one instance including the effective JVM flags, recomputed without launching.
  • instances.snapshot / instances.restore: tagged point-in-time copies of mods/config for safe experiments.
  • content.resolve: dry-run version resolution before committing to an install.

Observation: events, webhooks, metrics

  • Events: subscribe with server-side filters, e.g. only {"types": ["op.finished", "game.crash"]}. For headless game observation, enable game.capture first (before launching), then consume game.output and the semantic game.ready / game.crash / game.server_connected / game.player_chat events.
  • Webhooks (hooks.register, local-only): the launcher POSTs matching event lines to your URL with an optional HMAC-SHA256 signature (x-pandora-signature), so a service can react without holding a subscription. Hooks survive restarts.
  • Prometheus (metrics.prometheus): OpenMetrics text (pandora_up, pandora_uptime_seconds, pandora_instances_running, pandora_instance_rss_bytes{instance}); serve it from a scrape endpoint of your own. metrics.instances, metrics.storage, health.check and backend.ping cover the rest.
  • Dev loop: content.dev_link symlinks freshly built jars into an instance; content.bisect_start / bisect_step binary-search a broken modpack; diagnostics.analyze classifies crashes into likely causes with suggestions.

Driving Pandapi from an LLM

The API was shaped with agents in mind: a single self-description call, stable machine-readable errors, and an event stream that doubles as an observation channel.

A minimal agent loop:

  1. Bootstrap: call api.describe and put commands, events and security in the system context. The model now knows every command, what each does, and what its token can reach.
  2. Act: the model emits {"cmd": ..., "params": ...}; the harness executes it and returns the response verbatim, ok: true data and ok: false errors alike (the {code, message, retryable} object is designed to be reasoned over).
  3. Wait: when a response contains {"op": N}, the harness runs ops.wait and returns the finished op, including its error slot and any visit_url (e.g. Microsoft login needing a human).
  4. Observe: a second connection subscribed with a types filter (e.g. op.finished, game.ready, game.crash, notification) feeds events back into the context as observations.

Guardrails, all built in (see Network-Mode-and-Security):

  • Run the agent against the network transport with a scoped token rather than the fully-trusted local socket: read for observe-only agents; write without accounts/settings for most tasks.
  • The path jail bounds every path the agent can reference; execution-vector fields (jvm_flags, wrapper_command, ...) are refused over the network outright, so a prompt-injected agent cannot turn the launcher into an arbitrary-code runner.
  • The local-only set keeps credential minting, host control, webhook registration and the server pinger out of reach.

Today, an LLM harness with shell access (Claude Code or similar) can drive the CLI directly; any tool-use harness can wrap the socket in a generic pandapi(cmd, params) tool fed by api.describe.

MCP

A dedicated MCP server (pandapi-mcp) is planned as Phase 1 of the ecosystem roadmap: tools/resources auto-generated from api.describe, event and log resources (pandapi://events, pandapi://instance/logs), scope/redaction/path-jail inheritance, and loopback/tunnel transport. It is not shipped yet; until it lands, use the CLI or socket patterns above. See Ecosystem for the full plan.

The boundary

Pandapi launches and observes the real Minecraft client: it can start/kill the game, read the console and semantic events, and manage accounts, instances and content. It cannot inject in-game input or chat. Agents that need to act in-world (move, mine, chat) drive a headless bot framework alongside Pandapi; the hub-and-actors architecture for that is the subject of Ecosystem.