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
autofor 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:
- GET
/workflow-runs/{run_id} - Check
statusfield in response JSON - If
status == "done", break and return - Otherwise, sleep for
args.pollseconds (derived fromDEFAULT_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.requestfor zero-dependency HTTP operations against a local FastAPI server - Workflow submission sends multipart form data to
POST /workflow-runs/from-imageand receives arun_id - Status polling loops on
GET /workflow-runs/{run_id}untilstatusequals"done" - Health checks verify server availability via
GET /healthbefore costly operations - All logic lives in
tools/modly-cli/agent.pywith 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →