Skip to main content

swebot-client

Python client for the swebot autonomous coding agent.

Pure Python — zero dependencies beyond the standard library.

Install

# pip
pip install swebot-client

# uv
uv add swebot-client

Quick start

import swebot

print(swebot.ask("explain goroutines"))

That is the whole ceremony for a one-shot question against a backend on localhost. Stream it instead, when you want to watch it arrive:

for token in swebot.stream("explain goroutines"):
    print(token, end="", flush=True)

Take a session when the second question depends on the first — a session IS the context that makes a follow-up a follow-up:

with swebot.session(agent="developer") as s:
    s.send("read parser.go")
    print(s.send("what would you change about it?"))

Elsewhere, or behind a token:

swebot.connect("http://10.0.0.5:8080", token="…")   # or set SWEBOT_TOKEN

And the full client is one call away — everything below is reachable from it:

client = swebot.Client(timeout=30)
client.set_mode("autonomous")
session = client.create_session(agent="developer")

The same library under two names

import swebot is the front door; swebot_client is the same objects under the name existing scripts already import. They can be mixed in one program — swebot.TurnFailed is swebot_client.TurnFailed, not a copy of it — so nothing has to be rewritten to use the shorter form, and an except clause keeps catching either way.

from swebot_client import SwebotClient     # unchanged, and staying

Features

  • Streaming — real-time token-by-token output via SSE
  • Tool callbacks — observe tool invocations and results
  • Session management — create, resume, list, delete sessions
  • Mode / model control — switch modes, models, thinking, reasoning
  • Todos & checkpoints — read, create, update, delete todos; list/restore checkpoints
  • MCP management — configure MCP servers programmatically
  • Plugin management — install, remove, enable, disable plugins
  • AgentSpeak — multi-agent collaboration (join, leave, send messages)
  • Context control — compact or clear session context
  • Skills — list built-in skill agents

Examples

from swebot_client import SwebotClient, ToolCall

client = SwebotClient("http://localhost:8080")
client.wait_until_ready(timeout=10)

# Set mode to autonomous so the agent executes (not plans)
client.set_mode("autonomous")

# Enable extended thinking
client.set_thinking("high")

# Switch model
client.set_model("claude-opus-4-5")

# Create a session and send a message
session = client.create_session(agent="developer")

def on_tool(tc: ToolCall):
    print(f"  ⚙ {tc.name}")

reply = session.send_with_events(
    "create a hello.py file",
    on_tool_start=on_tool,
)

# Read todos
for todo in session.get_todos():
    print(f"[{todo.status}] {todo.title}")

# Token usage
usage = session.token_usage()
print(f"Tokens: {usage['tokens_in']:,} in / {usage['tokens_out']:,} out ({usage['usage_pct']}%)")

Pipeline example

from swebot_client import SwebotClient

client = SwebotClient()
client.wait_until_ready()
client.set_mode("autonomous")

# Stage 1: generate
s1 = client.create_session()
code = s1.send("Write a Python prime sieve for 0-1000000")

# Stage 2: review
s2 = client.create_session("code_auditor")
review = s2.send(f"Review this code:\n{code}")
print(review)

API reference

Module-level calls

Call Description
swebot.ask(message, agent="default", keep=False) One message in a session of its own → str. A full turn: the agent may act. For a read-only question use session.ask() or start the message with ?
swebot.stream(message, agent="default", keep=False) The same, token by token → Iterator[str]
swebot.session(agent="default", keep=True) A session, as a context manager. Kept by default — pass keep=False for a scratch one
swebot.connect(addr, token, …) Point those three at a different backend → the Client
swebot.connect("unix:///path/swebot.sock") A backend started with --bind unix:///path/swebot.sock, over its Unix socket. SwebotClient("unix:///…") takes the same address; every method works the same. See examples/26_unix_socket.py
swebot.client() The client they are using

A backend that is not running gives a ConnectionError naming the address and telling you to start one, rather than a bare URLError from urllib.

The session ask and stream create is scratch: you never see its id, nothing can resume it, and a script asking a thousand questions would leave a thousand sessions behind. It is discarded when the answer is in — unless the session is still doing something, in which case it is left alone:

  • a wake-up is scheduled — the turn asked to be resumed, and the session is how it comes back. (The same rule swebot's own one-shot CLI applies.)
  • the turn is still running — an autonomous or goal continuation outlives the stream that started it, so a one-shot that set one going leaves it working rather than deleting it mid-turn.
  • anything unknown — an older backend that does not report wake-ups, a request that fails. Deleting is the direction that cannot be undone.

The backend checks again as it deletes, so a wake-up scheduled by another backend between the check and the delete cannot be lost. keep=True holds on to the session regardless; swebot.session() is for when the conversation itself matters.

SwebotClient(addr="http://localhost:8080", token="", timeout=60.0, stream_timeout=None)

No token needed for a backend on your own machine. If it was started with --token (which a backend bound off loopback requires), set SWEBOT_TOKEN in your environment — the SDK reads it automatically.

timeout bounds ordinary requests, so a backend that accepts the connection and then stops answering cannot hang your script; wait_until_ready(timeout=…) passes its remaining time down to each probe. stream_timeout is separate and defaults to no deadline, because a turn streams for as long as it runs and a silent tool call of several minutes is normal.

Errors

A turn that fails — the provider erroring, a stall, a cancellation — raises TurnFailed from send(), stream() and send_with_events(). Whatever text arrived before the failure is on the exception as .partial:

from swebot_client import TurnFailed

try:
    answer = session.send("refactor the parser")
except TurnFailed as e:
    print("the turn failed:", e)
    print("partial output was:", e.partial)

These used to return the empty string, or the partial text, with nothing to distinguish a failed turn from a short answer.

ToolCall

What the agent did, as on_tool_start / on_tool_result see it: id, name, input, output, and two flags that are NOT the same question.

Field Means
is_error the call did not succeed
not_run there was no call — declined, blocked in plan mode, refused by a plugin, cancelled before it started

A refused tool is not a failed tool, and anything auditing what the agent actually did needs to tell them apart. examples/17_failures.py shows all three outcomes.

Sessions

Method Description
create_session(agent) Create a new session → Session
get_session(id) Resume an existing session → Session
list_sessions(here_only=False) List sessions → list[dict], each with workdir (where it was created), here (that is the backend's directory) and name (the backend name it last ran under, when it had one); here_only=True keeps just those
prompt_history(limit=None) Prompts a person typed, across all sessions, newest first and each once → list[str] (the TUI's ↑ history; default 500, at most 5000)
delete_session(id) Delete a session

Agents / Providers / Models

Method Description
list_agents() List available agents
get_agent(name) Get agent details (name, system_prompt, tools)
list_providers() List LLM providers
list_models(provider) List models for a provider → {provider, active, models}
set_model(model, provider) Switch model (and optionally provider)

Mode / Thinking / Reasoning

Method Description
get_mode() Current mode (plan, agent, autonomous)
set_mode(mode) Set mode: "plan", "agent", "autonomous"
get_thinking() Current thinking level
set_thinking(level) Set thinking: "", "low", "medium", "high"
get_reasoning() Current reasoning depth
set_reasoning(level) Set reasoning: "low", "medium", "high"

MCP Servers

Method Description
list_mcp() List MCP servers with connection state
add_mcp(name, ...) Add a new MCP server
update_mcp(name, ...) Update an existing server
delete_mcp(name) Remove a server
enable_mcp(name) Enable a disabled server
disable_mcp(name) Disable without removing
reload_mcp() Re-read config.yaml and reconnect all enabled servers

Scheduled work

Wake-ups (the agent's schedule tool) and cron jobs run in the backend, with or without a client connected — nothing here blocks on them. List and cancel them, and watch them fire with events(). See examples/19_scheduled.py.

Questions

session.ask("what does fitFrame do?") asks a question: it is send("? " + question). The backend answers at once, without the reasoning pre-analysis or the verify pass, and refuses every tool that writes or executes for that turn, so asking never changes files. session.ask(q, keep_mode=True) sends ?? q instead: the same quick answer, but the tools follow the session's mode rather than being blocked, so in autonomous mode the agent may change files if the answer needs it. See examples/20_questions.py.

Method Description
list_wakeups(session_id="") Pending wake-ups, soonest first → list[dict] (id, session_id, message, fire_at, repeat_seconds)
cancel_wakeup(id) Cancel a pending wake-up
list_cron() Cron jobs → list[dict] (id, schedule, message, enabled, last_run, next_run)
add_cron(schedule, message) Add a job: "every 5m", "daily at 10:00", "weekdays at 09:00", "once 2026-12-25 10:00"
enable_cron(id) / disable_cron(id) Turn a job on or off, keeping it
remove_cron(id) Delete a job
events(kinds=None, session_id="", timeout=None) Watch the backend's live events → Iterator[Event]; schedule_fired / cron_fired when one starts, message_complete for its reply. Ends at timeout

Plugins

Method Description
list_plugins() List installed plugins
install_plugin(url) Install from GitHub (owner/repo)
remove_plugin(name) Uninstall a plugin
enable_plugin(name) Re-enable a disabled plugin
disable_plugin(name) Disable without removing

AgentSpeak (Multi-Agent)

Method Description
agentspeak_status() Connection status → {connected, project, role, url}
agentspeak_projects() List visible projects
agentspeak_who() List agents in current project
agentspeak_messages(since) Fetch recent messages
agentspeak_join(url, project, role) Connect and join a project
agentspeak_leave() Disconnect from project
agentspeak_send(message, to) Send message (broadcast or targeted)

Backend Management

Method Description
health() Check if backend is reachable
wait_until_ready(timeout) Block until backend responds
stats() Backend stats (model, tokens, uptime)
version() Version info (version, commit, latest)
skills() List built-in skill agents
panic() Emergency stop — cancel all; the mode stays as it is
backend_inventory() Source-owned remote/backend definitions
backend_action(action, **fields) Explicit inventory/lifecycle/connect/uninstall action
deploy_capabilities() Authenticated headless deployment capability check; read-only, no credential transfer
update_backend() Update the backend to the latest release; it restarts in place (refused while turns run)
shutdown() Gracefully stop the backend

Session

Messaging

Method Description
stream(message) Stream tokens → Iterator[str]
send(message) Send and get full reply → str
send(message, on_question=fn) The same, answering the agent's ask_user questions with fn(question, choices) -> answer — also those of the run's continuation steps after send returns, until the run ends (0.14.16)
wait_until_idle(timeout=None) Wait until the session runs nothing; True once idle (0.14.16)
pending_questions() The ask_user questions this session waits on → [{id, question, choices, …}] (0.14.16)
ask(question) Ask a question: read-only, no reasoning passes → str
ask(question, keep_mode=True) Ask a question under the session's current mode (??): no reasoning passes, but in auto the agent may act → str
send_with_events(msg, on_token, on_tool_start, on_tool_result) Send with callbacks → str

Todos

Method Description
get_todos() List todo items → list[TodoItem]
create_todo(title) Create a new todo
update_todo(id, status) Update status: pending, in_progress, done
delete_todo(id) Delete a todo

Checkpoints

Method Description
list_checkpoints() List file checkpoints → list[dict]
wakeups() This session's pending wake-ups → list[dict]
restore_checkpoint(id) Restore a file to a previous checkpoint

Context

Method Description
fork_points() The prompts the session can be forked in front of → list[dict] (seq, created_at, source, preview, content)
fork(seq) A NEW session with the history before the prompt at seq — take an instruction back; this one is unchanged → Session. See examples/10_sessions.py
compact() Trigger context compaction
clear() Clear all session context
token_usage() THIS session's usage → {tokens_in, tokens_out, context_window, usage_pct} — falls when the session is compacted
backend_token_totals() The backend's cumulative counters across every session

Info

Method Description
refresh() Reload session data from backend
lsp_diagnostics(server) Cached project diagnostic details: file, line, severity and message; no discovery or automatic fixes
message_count Number of messages (property)

Publishing to PyPI

cd sdk/python
python3 -m build
UV_PUBLISH_TOKEN=pypi-YOUR-TOKEN uv publish dist/*

Requirements

  • Python 3.9+
  • A running swebot backend (swebot --serve)

License

MIT

Code review

Review what a session changed, then choose what to fix. Findings are verified by an adversarial second pass — only confirmed ones can be applied.

from swebot_client import SwebotClient

session = SwebotClient().create_session()
session.send("Add a ParseInt helper to util/parse.go")

rev = session.review(mode="informative")   # or "autonomous"
if rev.running:
    rev = session.wait_for_review(rev.id)

for f in rev.findings:
    print(f.id, f.severity, f.file, f.claim, "confirmed" if f.confirmed else "refuted")

keep = [f.id for f in rev.confirmed if f.blocking]
drop = [f.id for f in rev.confirmed if not f.blocking]
if keep:
    session.apply_review(rev.id, keep)
if drop:
    session.dismiss_findings(rev.id, drop, reason="not now")

A session that changed no files returns scope="out_of_scope" (or "nothing_to_review") without calling a model at all.

Inspect language-server reports

session.lsp_diagnostics("pyright") reads the backend's cached project diagnostics without starting a server or asking the agent to repair files. Each report includes file, line, column, severity and message. Missing imports can indicate environment setup, not code defects. See examples/22_lsp_diagnostics.py.

Headless deployment capability

client.deploy_capabilities() reads authenticated GET /api/deploy. headless_deploy >= 1 supports API-key-only all and independent remote headless authentication. This check neither deploys nor transfers credentials. An older backend's HTTP error is preserved: update it rather than falling back to legacy deployment that might copy OAuth tokens. See examples/23_deploy_capabilities.py. Use the TUI /backend remote auth REMOTE for interactive, masked login; no browser is launched automatically.

Interactive deployment HTTP protocol (NDJSON)

See examples/python/deploy_http.py for a complete standard-library client. Unlike the CLI remote_deploy_pipeline.py, it keeps the deployment stream open while sending separate answer requests, masks secret input and cancels on Ctrl+C/EOF. No new public SDK helpers or PyPI release: 0.14.5 already provides deploy_capabilities().

export SWEBOT_ADDR=http://localhost:8080
# Set SWEBOT_TOKEN securely if the source backend requires authentication.
python examples/python/deploy_http.py user@host:2222 --port 8080 --auth anthropic
python examples/python/deploy_http.py user@host --all --auth openai

All requests use the source backend's bearer token. SSH, downloads and saved credential reads run on that backend, not this client. Use trusted HTTPS or a protected local transport for credentials.

  1. Preflight: GET /api/deploy must return {"headless_deploy":1} (or a higher supported version). Reject 404/unsupported responses: a legacy backend may ignore auth options and copy OAuth tokens. Capability support does not guarantee deployment availability: POST returns 501 if the backend lacks its deployment binary callback.
  2. Start: POST /api/deploy with JSON:
    {"Target":"user@host:2222","Port":8080,"All":false,"Auth":true,"AuthProvider":"anthropic"}
    
    Target is the SSH destination (2222 is SSH port). Required Port is backend port, 1–65535. All selects saved Anthropic/OpenAI/Google/Alibaba API keys only, never OAuth/login tokens. Auth requests independent remote authentication; AuthProvider selects a provider, or empty string offers a menu. Auth-only (Auth:true, All:false) skips local credential transfer; All:true, Auth:true combines transfer and login. Without either, offer saved API keys individually.
  3. Read NDJSON (one JSON object per line, not SSE):
    {"text":"Installing binary…"}
    {"text":"Choose provider","question":"opaque-id","input":true,"choices":["anthropic","openai"],"secret":false,"url":"","code":""}
    {"text":"Enter API key","question":"another-id","input":true,"choices":null,"secret":true,"url":"","code":""}
    {"text":"Authorize manually","url":"https://provider.example/authorize","code":"DEVICE-CODE"}
    {"text":"","done":true,"failed":false}
    
    Illustrative events, not a fixed sequence or complete menu. Show text, URLs and device codes; do not auto-open a browser or use the clipboard. Events without question require no reply. Submit exact displayed choice strings. Mask secret:true input; never log answers, bodies or authorization headers.
  4. Answer on a separate connection: POST /api/deploy/answer with {"question":"opaque-id","value":"choice or secret"}; response {"accepted":true}. Legacy boolean yes still supports yes/no prompts; string value takes precedence. Questions are single-use; expired or consumed IDs return 404. Keep the original streaming request open.
  5. Finish/cancel: done:true, failed:false means deployment completed, not necessarily backend startup. Installation without usable credentials succeeds unstarted: preserve progress and follow its hints. failed:true carries sanitized failure text. EOF without done leaves outcome unknown. Closing the stream requests cancellation; the backend also enforces a 20-minute deadline. Completed installation/settings are not rolled back and existing backends are not stopped. Verify remote state before retrying after cancellation or transport failure.

Remote connection and lifecycle HTTP API

These authenticated routes also execute on the source backend. No public SDK stream/lifecycle helpers exist; the HTTP example uses urllib. Existing SwebotClient can use a proxy as its base URL without a new package release.

Request Response and semantics
POST /api/connect with {"Target":"user@host:2222","Port":8080} Returns {"id":"…"}; optional Token overrides destination token lookup. Does not start/stop a backend or prove readiness.
Requests under /api/connect/{id}/… Forward through private SSH stdio. Client base URL: source URL + /api/connect/{id}; use source token, proxy supplies destination token. Supports normal API and streaming requests.
DELETE /api/connect/{id} Returns {"closed":true}; removes proxy only, does not stop remote backend.
POST /api/deploy/lifecycle with {"Target":"user@host","Port":8080,"Operation":"start"} Returns {"started":true}. HTTP 502 startup errors can include code: configuration, credentials, port, launch, readiness, protocol.
Same route with Operation:"stop" Token-free version, instance, pid, status, sessions_known and optional latest_session. Status: stopped, shutting_down, not_running, exited. HTTP 200 is not proof shutdown completed; unknown session state is not an empty database. Caller must obtain explicit user consent.

Connections are process-local, expire after 24 hours and require the source backend to stay up. Requests forwarded through missing/expired proxies return 404; SSH transport failures return 502. Deletion is idempotent and returns {"closed":true} even for a missing or expired ID. Reconnect after expiry/source restart. Proxy deletion is not backend shutdown. The CLI pipeline demonstrates tasks against a running destination; installation-only success is not proof it is ready for those tasks.

Named backend inventory (0.14.6)

GET /api/backends returns remotes (name -> SSH target) and backends (REMOTE/NAME -> name/remote/workspace/port). POST /api/backends/action takes action and fields: remote-add (name,target), remote-remove/remove (name), save/create-local (backend, optional create_directory; create-local accepts auto_port to skip occupied/saved ports), refresh (optional remote name), prepare/start/stop/logs/ connect (name; create_directory for prepare). Stop is explicit caller authorization; HTTP does not add TUI consent. Refresh returns running/stopped/unreachable per key. Logs returns bounded logs; connect returns proxy id using source bearer token. Existing DELETE /api/connect/{id} closes proxy, not executor. New named databases are isolated; old data untouched. See examples/24_named_backends.py.

Existing HTTP/CLI clients remain compatible despite TUI slash consolidation. Deployment NDJSON adds optional Backend definition and InstallOnly; clients provide workspace and answer directory/credential decisions explicitly.

Authenticated SDK requests and streams reject HTTP redirects rather than forward bearer credentials. Use the backend URL directly, including its proxy base path.

Remote uninstall

Existing backend_action() also supports uninstall on an updated backend/target:

# Destructive operation: call only after obtaining user confirmation.
# Stop target swebot processes first. Normal mode preserves settings/sessions.
client.backend_action("uninstall", name="lab", target="user@host",
                      full=False, confirmed=True)
# full=True permanently removes standard swebot credentials, settings and databases.
# Workspace files/external databases and source inventory are not deleted.

The target must exactly match backend_inventory()["remotes"]["lab"]. Errors are not converted to success or retried with broader deletion. No new SDK method or package release is required for this action.

Ask mode (0.14.16)

ask is the backend's fourth mode, next to plan, agent and autonomous. It runs like autonomous — no tool approvals, and the agent keeps going until it is done — but a question the agent asks with its ask_user tool waits for an answer, as long as it takes, instead of being answered "decide yourself". A script that is the user answers them:

client.set_mode("ask")
reply = session.send("Add a config file. Ask me which format.",
                     on_question=lambda q, choices: choices[0] if choices else "YAML")

In ask mode send returns when the first turn hands off to its continuation steps; on_question keeps answering in the background until the run ends, so call session.wait_until_idle() before switching the mode back or exiting — also when send raised: a stream that dropped after the backend accepted the message leaves its run going. An answer the backend did not accept (a dropped connection) is sent again while the question is open. The responder answers only its own run: once another run claims the session it stops, so a later send is never answered with an earlier callback. on_question(question, choices) returns the answer; a callback that raises still answers ("could not be answered … make your best judgment"), so the turn never waits on a crashed script. Without on_question the question waits for another client — the TUI, an AgentSpeak dashboard — or for client.answer_question(id, answer) with an id from session.pending_questions(). The same goes for plan and agent mode, where ask_user has always waited.

Example: 29_ask_mode.py.

Structured one-shot output (0.14.17)

client.complete() is ONE model request (POST /api/complete): no session, no tools, no retry, no repair, no continuation. Session turns cannot promise that — a turn waits out an overloaded provider and asks again, resends with a lower reasoning effort when one is rejected, and retries an empty answer — so a script that must know what it paid for, or whether the provider enforced its JSON, uses this:

r = client.complete("Label: 'great battery'", json_schema=schema,
                    schema_name="label", max_output_tokens=2000)
if r.ok:
    print(r.parsed, r.attempts, r.usage.output_tokens, r.usage.reasoning_tokens)
else:
    print(r.status, r.error or r.validation_errors)
  • status — ok, invalid_json, schema_mismatch, refusal, truncated, incomplete, error or unsupported. Failures are results, not exceptions; only a malformed request (HTTP 400) raises. A stream that ends before the provider says why it stopped is incomplete, never ok.
  • json_schema — with schema_mode="strict" (default) the provider must enforce it, or the request is refused before generation (status="unsupported", attempts=0). schema_mode="validate" falls back to generating unconstrained and validating locally. schema_enforcement says which happened (provider_enforced / local_validation_only). The reply is always validated locally too; output is the raw text, never repaired — a fenced or chatty reply is invalid_json.
  • max_output_tokens — a provider-side cap; a provider that cannot enforce one refuses the request instead of running uncapped. Reasoning is generated INSIDE the cap on every current provider (output_cap["reasoning_counts"]), so a small cap can be spent entirely on thinking and end truncated with no text.
  • attempts — HTTP requests that reached the provider, counted at the transport: 1, or 0 when refused. request_ids and response_id are the provider's ids where it sends them.
  • usage — a Usage: input_tokens, cached_input_tokens (part of input), output_tokens, reasoning_tokens (part of output). None means not reported — Anthropic does not report reasoning separately, so it is None there, not 0; a response without a cache breakdown has cached_input_tokens=None, a stream cut off before its output total output_tokens=None. Figures the provider gave before a failure are kept. partial marks a failed, cancelled or interrupted request.

client.completion_capabilities() reports what the backend's provider enforces. Today: OpenAI (API key) and Codex (ChatGPT login) enforce both schema and cap through the Responses API; Anthropic (API key and Claude login) enforces the cap through max_tokens on every model and the schema through output_config.format on Claude Haiku 4.5, Sonnet 4.5, Opus 4.1 and later. Copilot, Gemini, Alibaba and local endpoints report unsupported for both until verified — use schema_mode="validate" there. The request uses the backend's current model and reasoning setting.

Session.last_usage is the same Usage for the last send(): every agent run its stream reported (a verify or compaction rerun, a continuation step), each summed over its model calls; reasoning forks and the verify critic are not included. There reported=False means swebot estimated the figures (about 4 characters a token), and reasoning_tokens is None unless every call reported it. The usage stays partial=True until the turn's stream_end arrives, so a turn cut off after a completion (EOF, timeout) never reads as whole.

Example: 30_structured_output.py.

Owner/observer attachments (0.14.8)

Session.attach(client_id=None, observe_only=False) negotiates owner/observer access and returns a scoped bearer token. It never replaces the shared client's backend credential. Create a separate SwebotClient with returned token, then a Session on that client. For SSH proxy URLs the token authenticates both source proxy and destination session. Never print it or share administrative credentials as though they were observer credentials.

state() exposes normal runtime snapshot with access_mode. renew_access() renews scoped access without promotion. release_access() releases it without cancelling work or deleting history. Retain a unique client_id across reconnects; do not share one identifier between independent clients. Lease expires after 90 seconds. SDK callers own their renewal loop (every 20 seconds) and release on exit; no hidden background heartbeat is started.

Example: 25_observe_session.py. This performs no model calls or mutations beyond attachment maintenance.

Files between the backend and this machine (0.14.12)

The TUI's "@" popup can save a backend file here and upload one there; a script does the same with three methods on SwebotClient. Backend paths are relative to the backend's working directory.

Method What it does
file_stat(path, include_ignored=False) name, dir, size (bytes), files — what a download would copy. A directory counts what the popup shows: no .git, git-ignored files only with include_ignored
download(path, dest_dir=".", include_ignored=False, timeout=None) Copies a file or directory into dest_dir and returns the local path. A directory arrives as one .tar.gz stream and is unpacked as a folder; an entry that would land outside dest_dir is refused
upload(local_path, dir=".", timeout=None) Copies a local file or directory (without .git; hard-linked names each as a file) into dir on the backend; returns path (what an @ mention names), name and size. A read-only attachment gets HTTP 403

Nothing is ever overwritten on either side: a second report.pdf becomes report (2).pdf. Both write under a hidden partial name and rename only when the copy is complete. timeout defaults to none: it is how long one read or write on the connection may wait, not a bound on the whole copy, which takes as long as it takes. The HTTP endpoints are GET /api/files/stat, GET /api/files/download (a file supports Range, so a client can resume) and PUT /api/files/upload?dir=&name=[&kind=dir].

Example: 27_files.py.

Images for the model (0.14.15)

Method What it does
upload_image(image, timeout=None) Saves an image (a local path, or the bytes) on the backend, as the TUI's Ctrl+V does with a screenshot; returns path, media_type, size. Put the path in a message — the model opens it with read_file, which returns PNG, JPEG, GIF and WebP files as images (scaled to at most 2000 px). Anything the model could not be shown is refused (HTTP 415/422)

The endpoint is POST /api/images with the image as the body. Saved images live in ~/.swebot/pastes/images/ on the backend and are deleted after 30 days.

Example: 28_images.py.

Cron responses add RFC3339 next_run_at/last_run_at; convert those instants to client-local time. Legacy fields remain backend-local, ambiguous strings.

Metadata

Release files for swebot-client 0.14.17

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for swebot-client 0.14.17
File Size Uploaded Provenance Actions
swebot_client-0.14.17.tar.gz 100.8 kB Without provenance Details for swebot_client-0.14.17.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for swebot-client 0.14.17
File Interpreter ABI Platform Actions
swebot_client-0.14.17-py3-none-any.whl Python 3 none any Details for swebot_client-0.14.17-py3-none-any.whl

Total release size: 147.2 kB

Release files / swebot_client-0.14.17.tar.gz

Download URL swebot_client-0.14.17.tar.gz
Size 100.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c4f2b3112a34e38ace6b6e506ad3fc1b4dc8c0acd65a76a2febd5aeb6bed7ee8
BLAKE2b-256 checksum
How to use checksums
959468af572eb929f005751d355cb9440242b1b4aee62eab1c2aa41733bb789a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / swebot_client-0.14.17-py3-none-any.whl

Download URL swebot_client-0.14.17-py3-none-any.whl
Size 46.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
85d88894d54c3de9ff68548160d41dc0f75e105879078f815f2fd397cf18e625
BLAKE2b-256 checksum
How to use checksums
027612ff3d84546c070abfcd31dd6499d5c09fc6b2c015c55325dd9c02c73e83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for swebot-client 0.14.17

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page