Skip to main content

Official Python SDK for the AnyFrame control plane.

Project description

anyframe

The official Python SDK for the AnyFrame control plane — point an agent at a repo, get a sandbox running Claude Code inside.

                                ┌──────────────────────────────┐
                                │  Agent  (repo, system prompt)│
                                │   ├── Skills                 │
                                │   ├── MCPs                   │
                                │   └── Connector toggles      │
   ┌──────────┐   anyframe SDK  └─────────────┬────────────────┘
   │   you    │ ───────────────────▶          │  build
   │ (python) │                               ▼
   └──────────┘   ┌──────────────────────────────────────────┐
                  │ Session (sandbox · chat · serve)         │
                  └──────────────────────────────────────────┘

User-level Connectors plug MCP servers (Linear, Sentry, …) in once and toggle them per-agent. Skills + MCPs ride with the agent into every session it boots.

Install

uv add anyframe

Quickstart

import anyframe

af = anyframe.AnyFrame()          # reads ANYFRAME_API_KEY + ANYFRAME_BASE_URL

agent = af.agents.create(name="demo", repo_url="tinyhq/box", install_cmd="bun install")
af.agents.build(agent.id)
af.agents.wait_for_build(agent.id)

session = af.sessions.create(agent_id=agent.id)
session = af.sessions.wait_until_running(session.id)
print(session.sandbox_url)

Authentication

.env in your project root, or shell environment:

ANYFRAME_API_KEY=afm_...
ANYFRAME_BASE_URL=https://api.anyfrm.com   # optional
ANYFRAME_LOG_LEVEL=INFO                    # set DEBUG for request tracing

Mint a key in the dashboard, or from a logged-in session with af.tokens.create(name=...).

Async

Every method exists on AsyncAnyFrame with the same signature, just await-ed.

import asyncio, anyframe

async def main():
    async with anyframe.AsyncAnyFrame() as af:
        me = await af.me()
        agents = await af.agents.list()

asyncio.run(main())

Agents

Agents are the unit of "what runs in the sandbox" — a repo, a system prompt, a permissions config.

af.agents.list()
af.agents.create(name="demo", repo_url="owner/name", install_cmd="bun install")
af.agents.get(agent_id)                # AgentDetail: includes skills, mcps, connectors, image
af.agents.update(agent_id, name="renamed")
af.agents.delete(agent_id)

Skills

Skills are bundles of instructions the agent loads at boot (think: "deploy this app", "review this PR").

af.agents.skills.list(agent_id)
af.agents.skills.create(agent_id, name="deploy", source="builtin", content={...})
af.agents.skills.update(agent_id, skill_id, enabled=False)
af.agents.skills.delete(agent_id, skill_id)

MCPs

MCPs configured inline on the agent — for one-off MCP servers that aren't worth setting up as a reusable connector.

af.agents.mcps.list(agent_id)
af.agents.mcps.create(agent_id, name="git", transport="http", config={"url": "..."})
af.agents.mcps.update(agent_id, mcp_id, enabled=False)
af.agents.mcps.delete(agent_id, mcp_id)

Connectors

User-level MCP connectors — configure once, then opt in per-agent via the connector-toggle API below.

af.connectors.list()
discovery = af.connectors.discover("https://mcp.linear.app/sse")
authorize = af.connectors.create_oauth(mcp_url=discovery.mcp_url, display_name="Linear")
# open authorize.authorize_url in a browser; callback completes server-side
af.connectors.create_bearer(mcp_url="...", display_name="...", token="...")
af.connectors.reauthorize(connector_id)
af.connectors.delete(connector_id)

Per-agent toggle (controls which connectors apply to one agent):

af.agents.connectors.list(agent_id)
af.agents.connectors.set(agent_id, connector_id, enabled=True)

Builds

Builds bake an agent's repo + dependencies into a cached sandbox image — required before a session can boot it.

af.agents.build(agent_id, force=False)      # queue a build
af.agents.build_status(agent_id)            # current state + cached image id
af.agents.builds(agent_id, limit=20)        # history
af.agents.build_log_url(agent_id, build_id) # signed R2 URL for the archived log
for event in af.agents.stream_build(agent_id, build_id):
    print(event.event, event.json())        # live SSE log frames
af.agents.wait_for_build(agent_id)          # blocks until succeeded / fails

Sessions

A session is one live sandbox. Lifecycle is booting → running → snapshotting → terminated; resume brings a terminated session back from its snapshot.

session = af.sessions.create(agent_id=agent.id, idle_timeout_s=300)
af.sessions.wait_until_running(session.id)
af.sessions.list()
af.sessions.get(session.id)
af.sessions.snapshots(session.id)
af.sessions.terminate(session.id)
af.sessions.resume(session.id)
af.sessions.delete(session.id)              # hard delete; requires terminated

Chat

Talk to the running agent. message and respond proxy verbatim to the in-sandbox chat server; events is the live SSE stream; transcript reads persisted history.

af.sessions.message(session.id, {"text": "deploy main to staging"})
for event in af.sessions.events(session.id, last_event_id=None):
    print(event.id, event.event, event.json())
af.sessions.transcript(session.id, since=0, limit=1000)
af.sessions.respond(session.id, {"decision": "approve", "tool_use_id": "..."})

Serve (preview server)

Launch a dev server inside the sandbox and tunnel its port out — useful for live previews of the thing the agent is building.

af.sessions.serve_start(session.id, cmd="bun dev", port=3000)
af.sessions.serve_status(session.id)        # serve_url is set when up
af.sessions.serve_logs(session.id, tail=200)
af.sessions.serve_stop(session.id)

Credentials

The control plane needs your Claude OAuth token (always) and a GitHub PAT (for private repos). It only ever shows you redacted views.

af.credentials.get()                        # set flag + last4
af.credentials.set_claude("sk-...")
af.credentials.set_github("ghp_...")
af.credentials.clear_claude()
af.credentials.clear_github()

Tokens

Manage the API keys this SDK uses. create returns the raw token exactly once — store it now.

af.tokens.list()
created = af.tokens.create(name="ci-bot")
print(created.token)                        # afm_...  one-time
af.tokens.revoke(created.id)

Errors

All errors derive from anyframe.AnyFrameError, so one except catches everything.

anyframe.AnyFrameError       # base
├── anyframe.APIError        # any non-2xx (status_code, message)
├── anyframe.AuthError       # 401 — bad / missing API key
├── anyframe.NotFoundError   # 404
├── anyframe.ConflictError   # 409 — e.g. delete on a running session
├── anyframe.ValidationError # 400/422 (carries field-level details)
├── anyframe.RateLimitError  # 429 (exposes retry_after)
└── anyframe.ServerError     # 5xx

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anyframe-1.0.0.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anyframe-1.0.0-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

Details for the file anyframe-1.0.0.tar.gz.

File metadata

  • Download URL: anyframe-1.0.0.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for anyframe-1.0.0.tar.gz
Algorithm Hash digest
SHA256 fb564ec4443ee70391ca47da9c1d9280fb590724685648fbdedac6ecb4106ca9
MD5 530f437f1b22498f61529c0626bb4f75
BLAKE2b-256 d55645a3c3697a1f32d292a7c774bd9bb270617f51191a28f9a53ccda4a0e6b1

See more details on using hashes here.

File details

Details for the file anyframe-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: anyframe-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 29.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for anyframe-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f6f918530030b56e0708ea9dd9e19c3ded42eedaa0d41e753fcc6b883c807477
MD5 f7979e9cc4fb8aeec0eddf6b57ca1f4d
BLAKE2b-256 13aa3f46f26c5f94ebf37efda7b6eae4add3dd4f0e967cc1a306b179425e8273

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page