BioArena CLI — manage tools, jobs, spaces, and more
Project description
Muni CLI
Command-line interface for BioArena. Manage tools, GPU jobs, spaces, canvas nodes, and account balance from the terminal.
HTTP uses stdlib urllib only. One runtime dependency (realtime, which pulls in Pydantic v2 — used to parse typed responses from the SDK schema). Requires Python 3.11+.
Install
# Recommended — isolated global install
pipx install .
# Or for development
pip install -e .
Both install the muni command and the muni Python package. pipx is preferred for regular use — it installs into its own virtualenv but makes the muni binary available globally on PATH.
Quick Start
# 1. Log in (no config needed — defaults are built in)
# Default: opens a browser to muni.bio/cli/authorize, you click Authorize,
# CLI captures the callback on a localhost port, done. No password in terminal.
muni login
# On a headless / SSH box with no browser:
muni login --device
# Prints a short code; open muni.bio/device on any device and enter it.
# CI / scripted (legacy email+password):
muni login --email user@example.com --password mypassword
# 3. Explore tools
muni tools
muni tools --category protein-design
muni tool bioarena_rfd3 --inputs
# 4. Run a job
muni run bioarena_rfd3 pdb_id=1UBQ num_designs=5
muni run bioarena_ipsae --batch ipsae_batch.jsonl
cat ipsae_batch.jsonl | muni run bioarena_ipsae --batch - --wait
# 5. Check status and get results
muni status job_<uuid>
muni results job_<uuid>
muni results job_<uuid> --fields sequence,iptm
muni results job_<uuid> --sequences
muni files job_<uuid>
muni download-all job_<uuid> -o ./results/
muni wait job_<uuid> job_<another-uuid>
# Or submit and wait in one step:
muni run bioarena_rfd3 pdb_id=1UBQ num_designs=5 --follow
# 6. Work with canvas nodes in a space
muni space use <space_id>
muni nodes
muni nodes search "PXDesign"
muni node show <node_id>
muni node show <node_id> --json
Job Identifiers
Every job the API creates has two IDs:
| ID | Format | Purpose |
|---|---|---|
job_id |
job_<uuid> |
DB primary key — the stable, provider-agnostic identifier. Shown first in all muni output. Prefer this. |
call_id |
bioarena-<tool>-job_<uuid> (Anyscale) or rw-<workflow-uuid> (Rowan) |
External tracker the compute provider knows the job as. Useful for correlating with provider dashboards or webhooks. |
All muni subcommands that take a job argument (status, results, files, logs, download, download-all, wait, job summary, job query) accept either form interchangeably — the API's getJob() branches on the job_ prefix. muni run prints Submitted: job_<uuid> by default; pass --json to see both fields.
Commands
Configuration
| Command | Description |
|---|---|
muni config |
Interactive setup for API URL, Supabase URL, and publishable key |
muni config show |
Show the active config profile, including the active space (masks long keys) |
muni config profiles |
List all configured profiles and show which is active |
muni config use <profile> |
Switch the default profile (creates it if it doesn't exist) |
Authentication
The CLI uses OAuth 2.0 against the muni app. The default flow is authorization code + PKCE with a localhost loopback — no password is ever typed in the terminal. Supabase Auth sits underneath; refresh tokens are scoped to this CLI installation and won't collide with web sessions.
| Command | Description |
|---|---|
muni login |
Browser + PKCE flow (default). Opens <app_url>/cli/authorize, captures the callback on 127.0.0.1 |
muni login --device |
RFC 8628 device code. Prints a code you enter at <app_url>/device on any device |
muni login --no-browser |
Print the authorize URL instead of launching a browser |
muni login --client-name "My laptop" |
Label the session in Connected Devices |
muni login -e EMAIL -p PASS |
Legacy email/password flow (CI/scripted use) |
muni login --password-flow |
Force the legacy flow with interactive prompts |
muni logout |
Revoke the CLI refresh token server-side and clear local credentials |
muni whoami |
Show current user email, user ID, profile name, credit balance, and active space |
CLI sessions can be listed and revoked from the web at /account → Connected devices.
Account
| Command | Description |
|---|---|
muni balance |
Show credit balance |
Spaces
Spaces are shared workspaces for organizing jobs and collaborating with other users.
| Command | Description |
|---|---|
muni spaces |
List your spaces and mark the active one |
muni space create <name> |
Create a new space |
muni space delete <space_id> |
Delete a space (owner only) |
muni space members <space_id> |
List members with email, role, and user ID |
muni space invite <space_id> <email> |
Invite a user to a space |
muni space use <space_id-or-name> |
Set the active space for canvas node commands |
The active space is persisted in your profile config (~/.muni/config.json). Once set, all muni nodes and muni node commands default to it. You can override per-command with --space-id.
Canvas Nodes
Canvas nodes are the visual elements on a space's canvas — structures, metrics, file trees, code, text, and more. Each node has a type, label, optional node script (tool configuration), and lifecycle state.
Listing & Searching
| Command | Description |
|---|---|
muni nodes |
List all canvas nodes in the active space |
muni nodes --type structure |
Filter by node type (structure, metric, filetree, text, code, json, markdown, image, molecule, note, results) |
muni nodes -n 100 |
Set result limit (default: 50) |
muni nodes --space-id <id> |
Override the active space for this command |
muni nodes search <query> |
Search nodes by keyword (matches label, type, tool name, PDB ID, original name, text, markdown, SMILES, and metric columns) |
muni nodes and muni nodes search now include canvas position, size, and connection count in both table and JSON output.
Inspecting
| Command | Description |
|---|---|
muni node read <node_id> |
Read a node's full state (script, data, connections, Python code) |
muni node read <node_id> --json |
Full node payload as JSON |
muni node content <node_id> |
Stream the node's output content from storage |
Creating
| Command | Description |
|---|---|
muni node create <title> --type <type> |
Create a new node (types include metric, code, json, structure, chem, chart, text, image, job, group) |
muni node create <title> --type code --python main.py |
Create a code node with main.py content |
muni node create <title> --type <type> --script script.ts |
Create with an inline script (compiled + executed for non-job nodes) |
Updating & Running
| Command | Description |
|---|---|
muni node edit <node_id> --title "New Title" |
Rename a node |
muni node edit <node_id> --script script.ts |
Update a node's script |
muni node edit <node_id> --python main.py |
Update a code node's main.py |
muni node run <node_id> |
Compile + execute the node, cascading downstream |
muni node run <node_id> --wait |
Execute and wait for completion (default timeout: 60s) |
muni node run <node_id> --timeout 300 |
Set wait timeout in seconds |
muni node move <node_id> <x> <y> |
Move a node to an absolute canvas position |
muni node select <page_id> <node_id>... |
Select one or more nodes on the canvas |
muni node focus <page_id> <node_id> |
Focus the canvas viewport on a node |
muni node connect <source_id> <target_id> |
Create a data edge between two nodes |
Deleting
| Command | Description |
|---|---|
muni node remove <node_id> |
Soft-delete a node (recoverable; disables connections) |
All node mutations route through /api/sdk, which stamps the audit trail and broadcasts realtime updates — open canvases reflect changes without a refresh.
Metric nodes
| Command | Description |
|---|---|
muni node metric-summary <node_id> |
Column names, detected types, and row count |
muni node metric-query <node_id> --columns col1,col2 --sort-by col1 --limit 50 |
Sort / filter / paginate tabular data |
muni node metric-select <node_id> <action> |
Check / uncheck rows; actions: get, select, deselect, clear |
Typical workflow — explore a space from the terminal:
# Set your working space + page
muni space use 137df9fd-2634-4a99-bf7e-499799677d15
muni page use <page_id>
# See what's on the canvas
muni nodes
muni nodes --type metric
# Search for a specific run
muni nodes search "PXDesign"
# Inspect a metric node
muni node read <node_id> --json | jq '.currentData.preview'
muni node metric-query <node_id> --columns rank,af2_iptm,sequence --sort-by af2_iptm
# Create a new code node and run it
muni node create "Analysis" --type code --python analysis.py
muni node run <node_id> --wait
Tool Discovery
| Command | Description |
|---|---|
muni tools |
List all available tools |
muni tools -q <keyword> |
Search tools by keyword |
muni tools -c <category> |
Filter by category (e.g. protein-design, peptide-science, structure-prediction) |
muni tool <name> |
Full tool info: description, category, reference URL, and all inputs |
muni tool <name> --inputs |
Input parameters table with types, defaults, required markers, and descriptions |
muni tool <name> --examples |
Usage examples with sample muni run commands |
Job Execution
| Command | Description |
|---|---|
muni run <tool> [KEY=VALUE ...] |
Submit a job. Returns the call_id immediately |
muni run <tool> --params-file request.json |
Load parameters from a JSON file |
muni run bioarena_ipsae --batch batch.jsonl |
Submit many IPSAE jobs from a JSONL file |
cat batch.jsonl | muni run bioarena_ipsae --batch - |
Submit a JSONL batch from stdin |
muni run bioarena_ipsae --batch batch.jsonl --submit-concurrency 10 |
Set server-side batch submission concurrency |
muni run <tool> ... --wait |
Submit and poll until completion (prints compact completion hints) |
muni run <tool> ... --follow |
Submit and poll with richer live status output (provider, runtime) |
muni run <tool> ... --timeout 300 |
Timeout in seconds for --wait/--follow (default: 1800) |
muni run <tool> ... --space-id S |
Associate job with a space |
muni run <tool> ... --title "My run" |
Set a human-readable job title |
muni run <tool> ... --dry-run |
Validate and print the resolved request payload without submitting |
muni run <tool> ... --no-validate |
Skip client-side schema validation before submitting |
muni run <tool> ... --log-lines 200 |
Number of log tail lines to show after a failed --follow run (default: 120) |
When using --wait or --follow, the CLI polls job status with escalating backoff (2s, 2s, 5s, 5s, then 10s intervals). In human mode, completion stays compact: instead of dumping huge result payloads, muni prints the result shape and the next useful command such as muni results <id> --fields ..., muni results <id> --sequences, or muni files <id>. On failure with --follow, the last N log lines are automatically shown.
Batch Submit
Batch submit is available in v1 for bioarena_ipsae.
Input format:
- JSONL only
- one JSON object per line
- each line must already contain the final tool arguments
- use
--batch -to read JSONL from stdin
Examples:
# Submit 60 IPSAE jobs in one API request
muni run bioarena_ipsae --batch ipsae_batch.jsonl
# Submit from stdin
cat ipsae_batch.jsonl | muni run bioarena_ipsae --batch -
# Submit and wait for the successful items
muni run bioarena_ipsae --batch ipsae_batch.jsonl --wait --timeout 600
Behavior:
- Batch submission is best effort: valid items submit, invalid items are reported individually.
--waitwaits only on successfully submitted call IDs.- exit code is non-zero if any batch item fails submission or any waited job fails/times out.
--followis not supported with--batch.- shared Anyscale job queues are enabled automatically for batched IPSAE runs.
Job Monitoring
| Command | Description |
|---|---|
muni jobs |
List recent jobs (default: 20 most recent) |
muni jobs -s completed |
Filter by status (submitted, running, completed, failed, cancelled) |
muni jobs --space-id S |
Filter by space |
muni jobs -n 50 |
Set result limit |
muni status <call_id> |
Check job status (includes type, provider, runtime, timestamps) |
muni wait <id1> <id2> ... |
Poll one or more jobs until they finish |
muni wait <id1> <id2> ... --json |
Return a compact per-job summary for scripting |
muni results <call_id> |
Get job results as JSON (prints "still running" for 202 responses) |
muni results <call_id> --fields sequence,iptm |
Project selected fields from large structured result sets |
muni results <call_id> --sequences |
Extract sequences from common protein-design result shapes |
muni wait and muni run --wait treat a job as finished when the job row reaches a terminal status (completed, failed, cancelled). In JSON mode, wait returns compact metadata such as status, job_type, updated_at, completed_at, and the detected result family, without embedding the full result payload.
Logs
| Command | Description |
|---|---|
muni logs <call_id> |
Fetch provider logs (default: last 200 lines) |
muni logs <call_id> --max-lines 500 |
Fetch more log lines |
muni logs <call_id> --follow |
Continuously poll logs until job reaches a terminal state |
muni logs <call_id> --follow --interval 10 |
Set polling interval in seconds (default: 5) |
File Management
| Command | Description |
|---|---|
muni files <call_id> |
List output files as a tree (with file sizes) |
muni files <call_id> --path designs |
Show only the designs/ subtree |
muni download <call_id> <path> |
Download a single file (saves to current directory) |
muni download <call_id> <path> -o out.pdb |
Download with a custom output path |
muni download <call_id> <path> --stdout |
Write a single file directly to stdout |
muni download <call_id> <path> -o - |
Alias for --stdout |
muni download <call_id> designs |
Download an entire directory (auto-detected) |
muni download-all <call_id> |
Download all files to ./<call_id>/ |
muni download-all <call_id> -o ./results/ |
Download all to a custom directory |
muni download-all <call_id> --path designs |
Download only the designs/ folder |
muni download-all <call_id> --glob "*.pdb" |
Download only .pdb files |
muni download-all <call_id> -p designs -g "*.json" |
Combine: only .json files under designs/ |
Binary files are base64-decoded on download. Text files are written as UTF-8.
Use --stdout only for single files. It is designed for piping into tools like jq, head, and grep, and cannot be combined with --json.
Typical workflow — inspect then download:
# See what the job produced
muni files job_<uuid>
# Download just the designs folder
muni download-all job_<uuid> --path designs -o ./my-designs/
# Or grab only PDB files from anywhere in the output
muni download-all job_<uuid> --glob "*.pdb" -o ./structures/
# Or stream one file directly into another tool
muni download job_<uuid> summary.json --stdout | jq '.'
Global Flags
These flags work with any subcommand:
| Flag | Description |
|---|---|
--json |
Output machine-readable JSON instead of human-formatted tables |
--profile <name> |
Use a specific config/credentials profile for this command |
--api-url <url> |
Override the API URL for this command only |
--supabase-url <url> |
Override the Supabase URL for this command only |
--supabase-publishable-key <key> |
Override the Supabase publishable key for this command only |
Examples:
muni config show --json
muni space members <space_id> --json
Parameter Syntax
Parameters use KEY=VALUE format on the command line. Values are auto-parsed:
# Strings
muni run bioarena_rfd3 pdb_id=1UBQ
# Numbers (auto-detected via JSON parsing)
muni run bioarena_rfd3 pdb_id=1UBQ num_designs=10
# Booleans
muni run some_tool flag=true
# JSON arrays
muni run bioarena_rfd3 hotspot_residues='["A32","A35"]'
# JSON objects
muni run some_tool config='{"key": "value"}'
File References
Use @ to load parameter values from files:
# Auto-detect: JSON files parsed as JSON, others read as text
muni run bioarena_rfd3 target_pdb=@./my_protein.pdb
muni run bioarena_protenix input_json=@./request.json
# Force JSON parsing
muni run bioarena_protenix input_json=@json:./request.json
# Force text (no JSON parsing)
muni run bioarena_rfd3 target_pdb=@text:./target.pdb
Params File
Load all parameters from a JSON file (can be combined with KEY=VALUE args, which take precedence):
muni run bioarena_protenix --params-file ./request.json
muni run bioarena_protenix --params-file ./request.json num_designs=20 # override
Client-Side Validation
By default, muni run fetches the tool's JSON Schema and validates parameters locally before submitting. This catches type mismatches, unknown parameters, and missing required fields. Disable with --no-validate.
--validate here is client-side schema validation only. It does not perform a remote execution preflight against staged files or backend job readiness.
Result Shapes
Different tools store different result families in Supabase. Common shapes include:
- record arrays such as
rankings,top_designs,sequences,designs, and some array-shapedmetrics - scorecard objects such as
best_metrics,metrics_summary,ranking, object-shapedmetrics, and object-shapedsummary - file-heavy manifests such as
pdb_files,summary_files,csv_files,json_files, and related output lists
muni results --fields works best for structured record arrays and scorecard objects. muni results --sequences is a convenience extractor for common protein-design outputs. For file-heavy jobs, start with muni files and then muni download or muni download-all.
Configuration
Config is stored in ~/.muni/config.json with named profile support:
{
"current_profile": "default",
"profiles": {
"default": {
"api_url": "https://api.bioarena.com",
"supabase_url": "https://xxx.supabase.co",
"supabase_publishable_key": "sb_publishable_..."
},
"local": {
"api_url": "http://localhost:8000",
"supabase_url": "https://xxx.supabase.co",
"supabase_publishable_key": "sb_publishable_..."
}
}
}
Credentials are stored separately in ~/.muni/credentials.json per profile. Both files are created with 0600 permissions.
Environment Variables
Environment variables override config file values:
| Variable | Description |
|---|---|
MUNI_API_URL |
BioArena Tools API URL |
MUNI_SUPABASE_URL |
Supabase project URL |
MUNI_SUPABASE_PUBLISHABLE_KEY |
Supabase publishable key |
MUNI_SPACE_ID |
Override the active space for canvas node commands |
MUNI_PROFILE |
Select a saved config/credentials profile |
Token Management
Authentication tokens are automatically refreshed when within 60 seconds of expiry. If the refresh token is missing or invalid, you'll need to muni login again.
Python SDK
Use MuniClient programmatically for scripting and automation. The preferred API is the namespaced sub-clients — they return Pydantic v2 models parsed from the TypeScript SDK's Zod schemas, so IDE autocomplete and validation work out of the box. The flat methods (client.list_tools(...), client.run(...), etc.) remain available as stable dict-returning wrappers.
from muni import MuniClient
client = MuniClient()
# ── Sub-clients (preferred, typed) ────────────────────────────────
# Spaces
spaces = client.spaces.list() # list[SpacesListOutputItem]
print(spaces[0].name, spaces[0].id)
my_space = client.spaces.create("Experiments") # SpacesCreateOutput
# Pages
pages = client.pages.list(my_space.id) # list[PagesListOutputItem]
default = client.pages.get_default(my_space.id) # PagesGetDefaultOutput
page_id = default.id
# Tools
tools = client.tools.list(category="protein-design") # list[ToolsListOutputItem]
tool = client.tools.get("bioarena_rfd3") # ToolsGetOutput (includes parameters, reference_url)
# Jobs
submit = client.jobs.submit(
"bioarena_rfd3",
{"pdb_id": "1UBQ", "num_designs": 5},
title="RFD 1UBQ",
space_id=my_space.id,
) # JobsSubmitOutput
results = client.jobs.wait(submit.call_id, timeout=600)
# Nodes (return dicts — augmented with runtime state post-create)
node = client.nodes.create(page_id, "Analysis", type="code",
python_code="print('hi')")
client.nodes.run(node["nodeId"])
# Connections
conn = client.connections.create(
page_id=page_id,
target_node_id=node["nodeId"],
target_port="input",
source_node_id="node_src",
source_port="output",
)
# ── Flat wrappers (legacy; emit snake_case dicts) ─────────────────
tools_dict = client.list_tools(category="protein-design") # {"tools": [...]}
job = client.run("bioarena_rfd3", {"pdb_id": "1UBQ", "num_designs": 5})
call_id = job["call_id"]
def on_status(data):
print(f"Status: {data['status']}")
results = client.wait_for_job(call_id, timeout=600, on_status=on_status)
client.save_all_files(call_id, "./output/", glob_pattern="*.pdb")
Sub-clients
| Namespace | Example methods |
|---|---|
client.user |
whoami(), balance() |
client.spaces |
list(), get(), create(), delete(), members(), invite(), resolve() |
client.pages |
list(), get(), get_default(), create(), delete(), reorder() |
client.nodes |
list(), get(), create(), update(), remove(), run(), save(), move(), select(), focus(), metric_summary(), metric_query() |
client.node_files |
list(), get(), create(), update() (Python files for code nodes) |
client.node_data |
list(), get() (metric data) |
client.connections |
list(), create(), remove(), enable(), disable() |
client.tools |
list(), get(), examples() |
client.jobs |
submit(), status(), wait(), results(), search(), cancel(), summary(), query(), logs(), batch() |
client.files |
list(), get(), download() |
client.chat / client.plans / client.sandbox / client.scripts |
see muni/clients/ |
Each sub-client routes through POST /api/sdk on the app server and (where wired) returns Pydantic models from muni._generated.models. Call .model_dump(by_alias=True) on any model to get a camelCase dict matching the TypeScript SDK's response shape.
Flat MuniClient methods (legacy, stable)
| Method | Returns | Description |
|---|---|---|
list_tools(query, category) |
dict |
List/search available tools (Tools API) |
get_tool(name) |
dict |
Get tool schema (Tools API) |
get_examples(name) |
dict |
Get usage examples (Tools API) |
run(tool, args, space_id, title) |
dict |
Execute a tool, returns {"call_id": "..."} |
run_batch(tool, items, space_id, title_prefix, submit_concurrency) |
dict |
Execute many items in one API request |
job_status(call_id) |
dict |
Check job status |
job_results(call_id) |
(int, dict) |
Get results (HTTP 202 = still running) |
job_logs(call_id, max_lines) |
dict |
Fetch provider logs |
cancel_job(call_id) |
(int, dict) |
Cancel a running job |
wait_for_job(call_id, timeout, on_status) |
dict |
Poll until completion, raises on failure/timeout |
list_jobs(space_id, status, limit) |
list[dict] |
List jobs from Supabase |
list_files(call_id) |
dict |
List output files |
download_file(call_id, path) |
(str, bool) |
Download file content (content, is_text) |
save_file(call_id, path, dest) |
Path |
Download and write file to disk |
save_all_files(call_id, output_dir, *, path_prefix, glob_pattern) |
list[Path] |
Bulk download, optionally filtered |
get_balance() |
dict |
Credit balance (snake_case keys) |
list_spaces() / create_space(name) / delete_space(id) |
— | Space management (snake_case dicts) |
space_members(space_id) / invite_to_space(space_id, email) |
— | Membership |
resolve_space_id(id_or_name) |
str |
Resolve a UUID or name to a space UUID |
list_pages(space_id) / get_page(page_id) |
— | Page lookup (PostgREST) |
search_nodes(page_id, *, node_type, query, limit) |
list[dict] |
Search nodes on a page |
create_node(page_id, title, *, node_type, script, python_code, position_x, position_y) |
dict |
Create a node (delegates to client.nodes.create) |
read_node(node_id) / edit_node(...) / remove_node(node_id) |
dict |
Node management |
run_node(node_id, *, wait, timeout) |
dict |
Execute a node, optionally polling for completion |
metric_summary(node_id) / metric_query(node_id, ...) / metric_select(node_id, action, ...) |
dict |
Metric node helpers |
Exceptions
from muni import MuniError, AuthError, NotFoundError, JobFailedError, JobTimeoutError
from muni.exceptions import APIError, JobCancelledError
try:
results = client.wait_for_job(call_id, timeout=300)
except JobFailedError as e:
print(f"Job {e.call_id} failed: {e.error}")
except JobTimeoutError as e:
print(f"Job {e.call_id} didn't finish in {e.timeout}s")
except JobCancelledError as e:
print(f"Job {e.call_id} was cancelled")
except AuthError:
print("Not logged in or token expired")
| Exception | Description |
|---|---|
MuniError |
Base class for all muni errors |
AuthError |
Login failed or no valid credentials |
NotFoundError |
Tool, job, or file not found |
APIError |
Unexpected HTTP error (has .status_code) |
JobFailedError |
Job completed with failed status (has .call_id, .error) |
JobCancelledError |
Job was cancelled (has .call_id) |
JobTimeoutError |
Timed out waiting for job (has .call_id, .timeout) |
Architecture
Derivation chain
The TypeScript SDK in muni-app is the canonical contract. The Python SDK is derived from it, and the CLI wraps the Python SDK:
TypeScript SDK (canonical) → Python SDK (derived) → CLI (wraps SDK)
muni-app/src/lib/ muni/clients/*.py muni/cli.py
muni/sdk/client/ muni/_generated/models.py
Pydantic v2 models in muni/_generated/models.py are generated from the app's OpenAPI/Zod schemas — do not edit by hand. Regenerate via pnpm codegen:python in muni-app.
Three backends
- App server
/api/sdk(canonical) — typed namespaced calls for nodes, pages, spaces, tools, jobs, connections, user, chat, plans, files, sandbox, scripts. Routed throughSDKClientBase._call(). - Tools API (Elysia server, port 8000) — legacy path still used by
muni run,muni results,muni logs,muni download, and the flatlist_tools/get_toolwrappers. - Supabase PostgREST (direct) — auth,
muni jobslisting, and a handful of direct reads/writes onmuni_nodes/muni_node_datafor metric query/select.
muni login → Supabase Auth API → ~/.muni/credentials.json
muni run → Tools API (POST /tools/:name/execute)
muni status → Tools API (GET /jobs/:callId/status)
muni spaces → App server /api/sdk (spaces.list)
muni nodes → App server /api/sdk (nodes.list)
muni node ... → App server /api/sdk (nodes.create/read/edit/run/...)
muni jobs → Supabase PostgREST (GET /rest/v1/jobs)
muni tools → Tools API (GET /tools)
All HTTP uses stdlib urllib. The only external dependency is realtime (used for push-based job-status updates in muni wait), which transitively pulls in Pydantic v2 for typed SDK responses.
Project Structure
muni-cli/
├── muni/
│ ├── __init__.py # MuniClient + exceptions re-export
│ ├── __main__.py # python -m muni entry point
│ ├── cli.py # argparse subcommands + KEY=VALUE parsing
│ ├── client.py # MuniClient facade (legacy flat methods + sub-client wiring)
│ ├── clients/ # Namespaced sub-clients (nodes, spaces, pages, jobs, tools,
│ │ │ # user, connections, files, node_files, node_data, chat,
│ │ │ # plans, sandbox, scripts) — all route through /api/sdk
│ │ └── _base.py # SDKClientBase: POST /api/sdk + optional Pydantic parsing
│ ├── _generated/ # Pydantic v2 models generated from the TS Zod schemas
│ │ └── models.py # DO NOT EDIT — regenerate via `pnpm codegen:python`
│ ├── auth.py # Email/password + PKCE login, logout, token refresh
│ ├── oauth.py # OAuth 2.0 + PKCE + device-code flows
│ ├── realtime.py # Supabase realtime subscriber for job-status updates
│ ├── api.py # Tools API HTTP calls
│ ├── supabase.py # PostgREST + Auth HTTP calls
│ ├── display.py # Human-readable formatting (tables, trees, status)
│ ├── config.py # ~/.muni/ file management + profile switching
│ ├── canvas.py # Node connection geometry
│ ├── node_scripts.py # Node script compile/validate helpers
│ ├── node_sources.py # Node source DSL parser
│ └── exceptions.py # MuniError hierarchy
├── pyproject.toml # PEP 621 packaging (setuptools)
├── scripts/push # Version-bumping push script
└── README.md
Versioning
Version is defined in muni/__init__.py (__version__) and read dynamically by pyproject.toml. Check it with:
muni --version
Auto-bump on push
Use ./scripts/push instead of git push to automatically bump the patch version after a successful push:
./scripts/push origin master
This will:
- Push your commits
- Bump the patch version (e.g.
0.1.3→0.1.4) - Commit and push the version bump
For major or minor bumps, edit muni/__init__.py manually before pushing.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file muni-0.1.13.tar.gz.
File metadata
- Download URL: muni-0.1.13.tar.gz
- Upload date:
- Size: 108.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
560731efa5778606833e01bba857eece9dfbdfbb48c0092f6184e3e5a1e32a3b
|
|
| MD5 |
5ff38e2fba8c82c067e0e59583d645b7
|
|
| BLAKE2b-256 |
f0e8ac16075ce7d402707e330abbf4de62b1440eb700508c905d60e62cd3a7f3
|
Provenance
The following attestation bundles were made for muni-0.1.13.tar.gz:
Publisher:
publish.yml on bioArena/muni-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
muni-0.1.13.tar.gz -
Subject digest:
560731efa5778606833e01bba857eece9dfbdfbb48c0092f6184e3e5a1e32a3b - Sigstore transparency entry: 1344894562
- Sigstore integration time:
-
Permalink:
bioArena/muni-cli@4798d9703d49a439b1e289519fed6a6a3212f198 -
Branch / Tag:
refs/tags/v0.1.13 - Owner: https://github.com/bioArena
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4798d9703d49a439b1e289519fed6a6a3212f198 -
Trigger Event:
push
-
Statement type:
File details
Details for the file muni-0.1.13-py3-none-any.whl.
File metadata
- Download URL: muni-0.1.13-py3-none-any.whl
- Upload date:
- Size: 98.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d8e35ecef736ef5ab1ab00c8b68f169c3e9a13bc6c3a04a650454d8860269f3
|
|
| MD5 |
9e4a57eb18028d470a9a24345cd109d3
|
|
| BLAKE2b-256 |
c3fd98199fcd2546db4a75e908e53c8302b3a2519bbec6e1df1aa1d089a87ca2
|
Provenance
The following attestation bundles were made for muni-0.1.13-py3-none-any.whl:
Publisher:
publish.yml on bioArena/muni-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
muni-0.1.13-py3-none-any.whl -
Subject digest:
4d8e35ecef736ef5ab1ab00c8b68f169c3e9a13bc6c3a04a650454d8860269f3 - Sigstore transparency entry: 1344894671
- Sigstore integration time:
-
Permalink:
bioArena/muni-cli@4798d9703d49a439b1e289519fed6a6a3212f198 -
Branch / Tag:
refs/tags/v0.1.13 - Owner: https://github.com/bioArena
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4798d9703d49a439b1e289519fed6a6a3212f198 -
Trigger Event:
push
-
Statement type: