Skip to content

Commit aa8bb61

Browse files
committed
feat(dev): add docker-free local mode (./dev.sh local)
Stand up the full backend as host processes with embedded datastores — no Docker daemon required — as a lighter alternative to the container stack. `./dev.sh local` (also `make dev-local` / `python -m scripts.dev_local`) provisions: - Postgres via bundled pgserver (unix socket) and Redis via bundled redislite - a Temporal dev server + UI and the agentex worker (--no-temporal to skip) - a local mongod, required for the full stack (--no-mongo / --lean to skip) - an optional OpenTelemetry collector (--no-otel to skip) then runs migrations, supervises uvicorn + the worker, and tears everything down cleanly on SIGINT/SIGTERM. --lean is a minimal Postgres+Redis+API stack; --ephemeral uses a throwaway data dir. The runner is a small scripts/dev_local package (config / services / supervise / runner) so the pure config/env layer stays testable. App-side changes to make the no-Docker path robust (all no-ops when Mongo is configured, as it always is in Docker/prod): - The Temporal worker no longer crashes when MongoDB is unavailable — the Mongo CRUD adapter tolerates an unset database and errors only on real use, so the worker degrades like the API instead of taking down the stack. - Skip the Mongo connection entirely when MONGODB_URI is unset, removing a ~20s startup hang against the implicit localhost:27017 default. - In local mode the backend rewrites agents' host.docker.internal ACP host to loopback (AGENTEX_ACP_HOST_OVERRIDE), so default-scaffolded agents work without manifest edits. Also fix a frontend dev-server process leak in dev.sh (kill the whole make→npm→next tree and sweep orphans; report status by listening port), correct the MongoDB and OpenTelemetry install commands, and document local mode in README and CLAUDE.md.
1 parent ab469df commit aa8bb61

17 files changed

Lines changed: 1311 additions & 53 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ tmp
4343
# Dev script logs
4444
.dev-logs/
4545

46+
# Local (no-Docker) dev datastore data
47+
agentex/.dev-local/
48+
4649
### PYTHON
4750

4851
# Byte-compiled / optimized / DLL files

CLAUDE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,27 @@ Other commands:
5555
./dev.sh restart # Restart all services
5656
```
5757

58+
Docker-free local mode (host processes + embedded datastores, no Docker):
59+
```bash
60+
./dev.sh local # whole stack without Docker (or: make dev-local)
61+
./dev.sh local --lean # Postgres + Redis + API + MongoDB only
62+
./dev.sh local --mongo-uri <uri> # use an external MongoDB instead of a local mongod
63+
```
64+
65+
> **MongoDB is required for the full local stack** and is always started — the Temporal
66+
> worker builds Mongo-backed repositories at startup, so a missing/unreachable Mongo
67+
> makes the runner fail fast (with an install message) rather than crash the worker.
68+
> `./dev.sh local` auto-installs `mongod`; `make dev-local` / direct `python -m
69+
> scripts.dev_local` do not, so install `mongod` yourself or pass `--mongo-uri <uri>` to
70+
> point at an external MongoDB. In local mode the Temporal UI is on :8233 (Docker mode
71+
> uses :8080).
72+
>
73+
> Agents register their ACP URL as `host.docker.internal` (for a Docker backend), which
74+
> a host-process backend can't resolve. Local mode sets `AGENTEX_ACP_HOST_OVERRIDE=127.0.0.1`
75+
> and the backend rewrites `host.docker.internal` → that value when dialing agents
76+
> (`src/utils/acp_url.py`, applied in the ACP request path + Temporal healthcheck), so
77+
> default-scaffolded agents work without manifest edits.
78+
5879
**Then in a separate terminal - Agent Development:**
5980
```bash
6081
agentex init # Create a new agent

README.md

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,37 +105,71 @@ To do this, you just need to spin up the [Agentex Server](https://github.com/sca
105105
106106
### Quick Start (Recommended)
107107

108-
Just run one command:
108+
Just run one command — no Docker required:
109109

110110
```bash
111-
./dev.sh
111+
./dev.sh local
112112
```
113113

114114
That's it. This will automatically:
115115
- Install Homebrew, uv, Node.js, and agentex-sdk if missing (macOS)
116116
- Install all backend and frontend dependencies
117-
- Start all Docker services (Postgres, Redis, MongoDB, Temporal)
118-
- Start the backend API and frontend dev server
117+
- Start the backend API and frontend dev server as host processes
118+
- Provision embedded datastores — bundled Postgres + Redis, an auto-downloaded Temporal
119+
dev server, a local MongoDB, and an optional OTel collector
119120
- Wait for everything to be healthy
120121

121-
> **Note:** Make sure Docker Desktop or Rancher Desktop is running before you start.
122+
> Prefer containers? `./dev.sh` runs the same stack with Docker instead (needs Docker
123+
> Desktop or Rancher Desktop) — see [Other commands](#other-commands) below. See
124+
> [Docker-free local mode](#docker-free-local-mode) for `local` flags and details.
122125
123126
Once ready:
124127
| Service | URL |
125128
|---------|-----|
126129
| Frontend UI | http://localhost:3000 |
127130
| Backend API | http://localhost:5003 |
128131
| Swagger Docs | http://localhost:5003/swagger |
129-
| Temporal UI | http://localhost:8080 |
132+
| Temporal UI | http://localhost:8233 |
130133

131-
**Other commands:**
134+
> With `./dev.sh` (Docker) the Temporal UI is on http://localhost:8080 instead.
135+
136+
#### Other commands
132137
```bash
138+
./dev.sh # Start everything with Docker instead (containers; needs Docker Desktop/Rancher)
133139
./dev.sh stop # Stop all services
134140
./dev.sh status # Check service status
135141
./dev.sh logs # View all logs
136142
./dev.sh restart # Restart all services
137143
```
138144

145+
#### Docker-free local mode
146+
147+
`./dev.sh local` accepts flags to trim the stack:
148+
149+
```bash
150+
./dev.sh local # whole stack, no Docker
151+
./dev.sh local --lean # Postgres + Redis + API + MongoDB only (no Temporal/OTel)
152+
./dev.sh local --no-temporal # skip Temporal + the worker
153+
./dev.sh local --mongo-uri <uri> # use an external MongoDB instead of a local mongod
154+
```
155+
156+
> **MongoDB is required for the full local stack** and is always started. The Temporal
157+
> worker needs it, so `./dev.sh local` auto-installs `mongod` (via Homebrew) and startup
158+
> **fails fast** with an install message if it can't be made available. Point at an
159+
> existing MongoDB with `--mongo-uri <uri>` to skip the local `mongod`. The OTel
160+
> collector is optional; if it's absent the runner continues without telemetry. In local
161+
> mode the Temporal UI is at http://localhost:8233 (not :8080). Same `stop` / `status` /
162+
> `logs` / `restart` commands apply.
163+
164+
**Connecting agents in local mode.** Agents scaffolded by `agentex init` register their
165+
ACP URL as `http://host.docker.internal:<port>` (so a *Docker* backend can reach an
166+
agent on the host). A host-process backend can't resolve that name, so local mode sets
167+
`AGENTEX_ACP_HOST_OVERRIDE=127.0.0.1` and the backend automatically rewrites
168+
`host.docker.internal``127.0.0.1` when dialing agents (and their healthchecks). So a
169+
default-scaffolded agent works with `./dev.sh local` **without editing its manifest**.
170+
(You can still set `local_development.agent.host_address: localhost` in the manifest if
171+
you prefer; both work.)
172+
139173
Then skip ahead to [Create Your First Agent](#create-your-first-agent).
140174

141175
---

agentex/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,19 @@ dev: install-dev ## Start development server with Docker Compose
4141
@echo "🚀 Starting development server with Docker Compose..."
4242
docker compose up --build
4343

44+
dev-local: install-dev ## Start development server locally (no Docker)
45+
@echo "🚀 Starting development server locally without Docker..."
46+
@echo "ℹ️ MongoDB is REQUIRED for the full stack. If 'mongod' is not on your PATH,"
47+
@echo " install it (brew tap mongodb/brew && brew install mongodb-community) or point"
48+
@echo " at an external one with ARGS=\"--mongo-uri <uri>\". (Unlike './dev.sh local',"
49+
@echo " make does not auto-install it.)"
50+
uv sync --group dev --group dev-local
51+
uv run python -m scripts.dev_local $(ARGS)
52+
4453
dev-stop: ## Stop development server
4554
@echo "Stopping dev server"
4655
docker compose down
56+
# TODO: Add support for stopping local dev server
4757

4858
dev-wipe: ## Stop dev server and wipe DB
4959
@echo "Stopping dev server and wiping DB"

agentex/pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ dev = [
4343
"vulture>=2.14",
4444
"ruff>=0.3.4",
4545
]
46+
dev-local = [
47+
"pgserver>=0.1.4",
48+
"redislite>=6.2.912183",
49+
# SQLAlchemy's async engine needs greenlet at runtime. SQLAlchemy auto-installs
50+
# it on linux x86_64 (docker/prod) but NOT on macOS arm64, so the local runner
51+
# must pull it explicitly or /readyz and engine teardown fail.
52+
"greenlet>=3.2.3",
53+
]
4654
test = [
4755
"pytest>=8.3.3,<9",
4856
"pytest-asyncio>=1.0.0,<2",
@@ -52,6 +60,9 @@ test = [
5260
"factory-boy>=3.3.0,<4", # for test data factories
5361
"greenlet>=3.2.3",
5462
"asyncpg>=0.29.0",
63+
# Embedded datastores for dev_local.py tests (no testcontainers/docker needed)
64+
"pgserver>=0.1.4",
65+
"redislite>=6.2.912183",
5566
]
5667

5768
[tool.hatch.build.targets.sdist]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Run the agentex backend locally as host processes, with no Docker.
2+
3+
The engine behind `./dev.sh local` / `make dev-local`: it provisions embedded datastores,
4+
runs migrations, and supervises uvicorn (plus a Temporal worker), tearing everything down
5+
on Ctrl-C / SIGTERM. Postgres/Redis/Temporal need no system install (bundled /
6+
auto-downloaded). MongoDB is REQUIRED for the full stack — the Temporal worker builds
7+
Mongo-backed repositories at boot, so a missing/unreachable mongod fails fast instead of
8+
crashing the worker. It is always started; --mongo-uri points at an external MongoDB
9+
instead of launching a local mongod. The OTel collector is optional; --lean turns off
10+
temporal/otel (but keeps MongoDB).
11+
12+
Modules: `config` (pure, unit-testable), `services` (provisioning), `supervise`
13+
(subprocess plumbing), `runner` (orchestration).
14+
"""
15+
16+
from scripts.dev_local.config import (
17+
LOOPBACK,
18+
DevLocalConfig,
19+
build_arg_parser,
20+
build_env,
21+
resolve_config,
22+
)
23+
from scripts.dev_local.runner import main, run
24+
from scripts.dev_local.services import (
25+
provision_mongo,
26+
provision_otel,
27+
provision_postgres,
28+
provision_redis,
29+
provision_temporal,
30+
teardown_redis,
31+
)
32+
from scripts.dev_local.supervise import run_migrations, wait_for_health
33+
34+
__all__ = [
35+
"LOOPBACK",
36+
"DevLocalConfig",
37+
"build_arg_parser",
38+
"resolve_config",
39+
"build_env",
40+
"provision_postgres",
41+
"provision_redis",
42+
"provision_temporal",
43+
"provision_mongo",
44+
"provision_otel",
45+
"teardown_redis",
46+
"run_migrations",
47+
"wait_for_health",
48+
"run",
49+
"main",
50+
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Entry point: `python -m scripts.dev_local` (used by `./dev.sh local` / `make dev-local`)."""
2+
3+
from scripts.dev_local.runner import main
4+
5+
if __name__ == "__main__":
6+
main()

0 commit comments

Comments
 (0)