Fleet dispatch module (`forge_lcdl.dispatch.fleet`)

Optional leaf HTTP client for Forge Fleet. Consumers (for example forge-dark-factory) import it explicitly; forge-lcdl core (flow, runner, tasks, transport) does not import this module.

Updated

Fleet dispatch module (forge_lcdl.dispatch.fleet)

Optional leaf HTTP client for Forge Fleet. Consumers (for example forge-dark-factory) import it explicitly; forge-lcdl core (flow, runner, tasks, transport) does not import this module.

Boundary

  • stdlib only: urllib, json, time, hashlib
  • No new [project.dependencies]
  • Bearer tokens stay on FleetNode at call time; FleetJobRef never stores tokens

API

Symbol Role
FleetNode id, base_url, bearer_token, enabled, priority, max_cpu_percent, max_memory_percent
FleetJobRef node_id, base_url, job_id (no token)
load_fleet_servers(path=None, environ=None) Resolve node list
select_node(nodes, *, exclude_ids=()) Priority + health + ceiling selection
select_node_required(...) Raises RuntimeError when no node qualifies
submit_docker_argv_job(node, argv, *, session_id="", meta=None) POST /v1/jobs
upload_workspace(node, job_id, tar_gz_bytes) PUT /v1/jobs/{id}/workspace + X-Workspace-Archive-Sha256
poll_job(node, job_id, *, timeout_s, interval_s) Poll until terminal status
cancel_job(node, job_id) POST /v1/jobs/{id}/cancel

Config resolution

  1. FORGE_FLEET_SERVERS_FILE → JSON file path
  2. <cwd>/config/fleet-servers.json
  3. Single-node fallback: FORGE_FLEET_BASE_URL + FORGE_FLEET_BEARER_TOKEN (priority 0)

Node JSON matches Lenses fleet-settings.json nodes[] entries (paste from Studio).

Example

from forge_lcdl.dispatch.fleet import load_fleet_servers, select_node_required, submit_docker_argv_job

nodes = load_fleet_servers()
node, meta = select_node_required(nodes)
ref = submit_docker_argv_job(node, ["echo", "ok"], session_id="example")
print(ref.node_id, ref.job_id)  # no bearer in ref

Tests

pytest tests/dispatch/test_fleet.py -v

Uses a threaded http.server fake Fleet (no live listener required).