How Modly's CLI Communicates with a Running Instance and Polls for Workflow Execution

Modly's CLI communicates with a running desktop instance via HTTP to a local FastAPI server at http://127.0.0.1:8765, submitting workflow jobs via POST and polling status via GET until completion.

The lightningpixel/modly project ships with a lightweight, std-lib-only command-line client that lets external agents and scripts trigger 3D mesh generation without installing heavy dependencies. The entire communication layer is implemented in pure Python using urllib.request.

The Local HTTP Architecture

When the Modly desktop application launches, it starts an embedded FastAPI server. The CLI discovers this server through the MODLY_API_URL environment variable, defaulting to http://127.0.0.1:8765【1†L40-L42】.

The CLI module at tools/modly-cli/agent.py contains three core components:

  • Request helper — _request_json() for all HTTP traffic
  • Submission logic — _start_workflow_run() for job creation
  • Polling loop — _poll_workflow_run() for status monitoring

Building and Sending Requests

All API calls flow through _request_json() in agent.py. This function:


# From tools/modly-cli/agent.py

def _request_json(method: str, url: str, data=None, headers=None, timeout=DEFAULT_TIMEOUT_SECONDS):
    req = urllib.request.Request(url, method=method, data=data, headers=headers or {})
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8"))

Errors are wrapped in ModlyCliError with a structured code field for programmatic handling【1†L65-L85】.

Starting a Workflow Run

The _start_workflow_run() function constructs a multipart form POST to /workflow-runs/from-image. The payload includes:

  • Input image bytes
  • Model ID (or auto for automatic selection)
  • Collection name
  • Remeshing options
  • Export format preferences
modly-cli workflow-run start \
  --image ./my_photo.png \
  --model auto \
  --collection Agent \
  --remesh quad \
  --output ./result.glb

This maps to line 14-22 in agent.py, where the function builds the form body and extracts the returned run_id【1†L14-L22】.

Polling Loop Implementation

The _poll_workflow_run() function implements a blocking poll loop with configurable interval:

  1. GET /workflow-runs/{run_id}
  2. Check status field in response JSON
  3. If status == "done", break and return
  4. Otherwise, sleep for args.poll seconds (derived from DEFAULT_POLL_SECONDS) and repeat【1†L35-L50】

# Simplified polling logic from agent.py

def _poll_workflow_run(base_url, run_id, poll_seconds, verbose=False):
    while True:
        result = _request_json("GET", f"{base_url}/workflow-runs/{run_id}")
        if result.get("status") == "done":
            return result
        if verbose:
            print(json.dumps(result), file=sys.stderr)
        time.sleep(poll_seconds)

The CLI exposes this directly via:

modly-cli workflow-run status --run-id 12345

Which hits the same endpoint without the automatic export step【1†L95-L99】.

Health Verification

Before operations, the CLI calls _require_health() or _try_health() to verify server availability. These functions hit /health and validate the response【1†L66-L71】:

modly-cli health

This prevents wasted work when the Modly desktop app isn't running.

Complete Execution Flow


CLI → GET  /health                       (optional: verify server)
CLI → POST /workflow-runs/from-image     (submit job, receive run_id)
CLI → GET  /workflow-runs/{run_id}       (poll until status="done")
CLI → GET  /export/{fmt}?path=...        (download final mesh)

The std-lib-only design means the CLI runs on any Python 3 installation without requests, httpx, or other HTTP dependencies.

Timeout and Configuration

Two constants control CLI behavior:

Constant Default Purpose
DEFAULT_TIMEOUT_SECONDS 300 (5 min) Maximum wait for any single HTTP request
DEFAULT_POLL_SECONDS 2 Sleep interval between status checks

Override via --timeout and --poll flags or environment variables.

Summary

  • Modly CLI uses pure urllib.request for zero-dependency HTTP operations against a local FastAPI server
  • Workflow submission sends multipart form data to POST /workflow-runs/from-image and receives a run_id
  • Status polling loops on GET /workflow-runs/{run_id} until status equals "done"
  • Health checks verify server availability via GET /health before costly operations
  • All logic lives in tools/modly-cli/agent.py with clear separation between request building, submission, and polling concerns

Frequently Asked Questions

Why does Modly use urllib.request instead of requests?

Modly's CLI avoids external dependencies entirely. Using urllib.request from the Python standard library lets agents invoke the tool on minimal systems without pip-installing packages. The tradeoff is slightly more verbose request construction, wrapped in helper functions for maintainability.

What happens if the desktop app isn't running when I run a CLI command?

The CLI health check (_try_health() or _require_health()) will fail with a ModlyCliError indicating connection refused. Commands that require server communication exit early with a clear diagnostic message rather than hanging or crashing obscurely.

Can I change the poll interval or timeout for long-running workflows?

Yes. Both --poll (seconds between checks) and --timeout (seconds per HTTP request) are exposed as CLI flags. The defaults of 2 seconds polling and 300 seconds timeout can be adjusted per command or via MODLY_API_URL and related environment variables.

Does the polling loop consume significant CPU or network resources?

No. The loop explicitly time.sleep(poll_seconds) between requests, defaulting to 2 seconds. With JSON payloads under 1KB and idle CPU usage near zero, the CLI remains lightweight even when monitoring hour-long generation jobs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →