chore(orchestrator): MS21 complete — UI-001-QA and TEST-004 done
All checks were successful
ci/woodpecker/push/infra Pipeline was successful

This commit is contained in:
2026-03-01 07:52:41 -06:00
parent f4c1c9d816
commit 9a742aa634
11 changed files with 474 additions and 0 deletions

90
docker/OPENCLAW-FLEET.md Normal file
View File

@@ -0,0 +1,90 @@
# OpenClaw Agent Fleet
OpenClaw multi-agent deployment for Mosaic Stack using Docker Swarm and Portainer.
## Agent Roster
| Agent | Service | Primary Model | Role |
| ----------------- | ------------------- | --------------- | ---------------------------------- |
| jarvis-main | `jarvis-main` | `zai/glm-5` | Orchestrator / user-facing gateway |
| jarvis-projects | `jarvis-projects` | `zai/glm-5` | Development and coding tasks |
| jarvis-research | `jarvis-research` | `zai/glm-5` | Research and web search |
| jarvis-operations | `jarvis-operations` | `ollama/cogito` | Monitoring, health checks, alerts |
## Prerequisites
1. Docker Swarm initialized on the target host
2. Existing Docker network `mosaic-stack_internal` (external swarm network)
3. Z.ai API access key (`ZAI_API_KEY`)
4. Ollama reachable at `10.1.1.42:11434` for the `cogito` model
## Quick Start
### 1. Configure each agent env file
Set values in:
- `docker/openclaw-instances/jarvis-main.env`
- `docker/openclaw-instances/jarvis-projects.env`
- `docker/openclaw-instances/jarvis-research.env`
- `docker/openclaw-instances/jarvis-operations.env`
Required variables:
- `OPENCLAW_CONFIG_PATH=/config/openclaw.json`
- `ZAI_API_KEY=<your-zai-api-key>`
- `OPENCLAW_GATEWAY_TOKEN=<unique-token-per-agent>`
### 2. Generate unique gateway tokens
Generate one token per service:
```bash
openssl rand -hex 32
```
### 3. Deploy the fleet
From repo root:
```bash
docker stack deploy -c docker/openclaw-compose.yml jarvis
```
### 4. Verify service status
```bash
docker stack services jarvis
docker service logs jarvis-jarvis-main --tail 100
docker service logs jarvis-jarvis-projects --tail 100
docker service logs jarvis-jarvis-research --tail 100
docker service logs jarvis-jarvis-operations --tail 100
```
### 5. First-time auth (if required)
Exec into a container and run OpenClaw auth device flow:
```bash
docker exec -it $(docker ps -q -f name=jarvis-jarvis-main) sh
openclaw auth
```
You can also complete this in the Mosaic WebUI terminal (xterm.js).
## Management Commands
| Command | Description |
| ----------------------------------------------------------- | ---------------------- |
| `docker stack deploy -c docker/openclaw-compose.yml jarvis` | Deploy/update fleet |
| `docker stack services jarvis` | List services in stack |
| `docker service logs jarvis-<service>` | View service logs |
| `docker service update --force jarvis-<service>` | Restart rolling update |
| `docker service scale jarvis-<service>=N` | Scale a service |
| `docker stack rm jarvis` | Remove fleet |
## Notes
- Each service stores persistent local OpenClaw state in `/home/node/.openclaw`.
- Each service mounts a read-only per-agent JSON config at `/config/openclaw.json`.
- `chatCompletions` endpoint is enabled in each agent config for Mosaic API usage.

166
docker/openclaw-compose.yml Normal file
View File

@@ -0,0 +1,166 @@
services:
jarvis-main:
image: alpine/openclaw:latest
command: ["gateway", "run", "--bind", "lan", "--auth", "token"]
env_file:
- ./openclaw-instances/jarvis-main.env
environment:
OPENCLAW_CONFIG_PATH: /config/openclaw.json
volumes:
- jarvis-main-config:/config/openclaw.json:ro
- jarvis-main-state:/home/node/.openclaw
networks:
- mosaic-stack_internal
healthcheck:
test: ["CMD", "openclaw", "gateway", "health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 2G
reservations:
memory: 512M
labels:
- com.mosaic.agent=jarvis-main
- com.mosaic.role=orchestrator
jarvis-projects:
image: alpine/openclaw:latest
command: ["gateway", "run", "--bind", "lan", "--auth", "token"]
env_file:
- ./openclaw-instances/jarvis-projects.env
environment:
OPENCLAW_CONFIG_PATH: /config/openclaw.json
volumes:
- jarvis-projects-config:/config/openclaw.json:ro
- jarvis-projects-state:/home/node/.openclaw
networks:
- mosaic-stack_internal
healthcheck:
test: ["CMD", "openclaw", "gateway", "health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 4G
reservations:
memory: 1G
labels:
- com.mosaic.agent=jarvis-projects
- com.mosaic.role=development
jarvis-research:
image: alpine/openclaw:latest
command: ["gateway", "run", "--bind", "lan", "--auth", "token"]
env_file:
- ./openclaw-instances/jarvis-research.env
environment:
OPENCLAW_CONFIG_PATH: /config/openclaw.json
volumes:
- jarvis-research-config:/config/openclaw.json:ro
- jarvis-research-state:/home/node/.openclaw
networks:
- mosaic-stack_internal
healthcheck:
test: ["CMD", "openclaw", "gateway", "health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 1G
reservations:
memory: 256M
labels:
- com.mosaic.agent=jarvis-research
- com.mosaic.role=research
jarvis-operations:
image: alpine/openclaw:latest
command: ["gateway", "run", "--bind", "lan", "--auth", "token"]
env_file:
- ./openclaw-instances/jarvis-operations.env
environment:
OPENCLAW_CONFIG_PATH: /config/openclaw.json
volumes:
- jarvis-operations-config:/config/openclaw.json:ro
- jarvis-operations-state:/home/node/.openclaw
networks:
- mosaic-stack_internal
healthcheck:
test: ["CMD", "openclaw", "gateway", "health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 1G
reservations:
memory: 256M
labels:
- com.mosaic.agent=jarvis-operations
- com.mosaic.role=operations
networks:
mosaic-stack_internal:
external: true
volumes:
jarvis-main-config:
driver: local
driver_opts:
type: none
o: bind
device: ${PWD}/docker/openclaw-instances/jarvis-main.json
jarvis-projects-config:
driver: local
driver_opts:
type: none
o: bind
device: ${PWD}/docker/openclaw-instances/jarvis-projects.json
jarvis-research-config:
driver: local
driver_opts:
type: none
o: bind
device: ${PWD}/docker/openclaw-instances/jarvis-research.json
jarvis-operations-config:
driver: local
driver_opts:
type: none
o: bind
device: ${PWD}/docker/openclaw-instances/jarvis-operations.json
jarvis-main-state:
jarvis-projects-state:
jarvis-research-state:
jarvis-operations-state:

View File

@@ -0,0 +1,47 @@
# OpenClaw Agent Instance Setup
Each service in the OpenClaw fleet reads:
- A per-agent environment file: `docker/openclaw-instances/<agent>.env`
- A per-agent JSON5 config: `docker/openclaw-instances/<agent>.json`
## 1. Fill in API keys in `.env` files
Set `ZAI_API_KEY` in each instance env file:
- `jarvis-main.env`
- `jarvis-projects.env`
- `jarvis-research.env`
- `jarvis-operations.env`
## 2. Generate unique gateway tokens per agent
Generate one token per instance:
```bash
openssl rand -hex 32
```
Set a different `OPENCLAW_GATEWAY_TOKEN` in each `.env` file.
## 3. Deploy the Docker Swarm stack
From repository root:
```bash
docker stack deploy -c docker/openclaw-compose.yml jarvis
```
## 4. First-time auth (if needed)
If an instance requires first-time login, exec into the running container and run:
```bash
openclaw auth
```
This uses OpenClaw's headless OAuth device-code flow.
## 5. Use Mosaic WebUI terminal for auth
You can complete the device-code auth flow from the Mosaic WebUI terminal (xterm.js) attached to the service container.

View File

@@ -0,0 +1,3 @@
OPENCLAW_CONFIG_PATH=/config/openclaw.json
ZAI_API_KEY=REPLACE_WITH_ZAI_API_KEY
OPENCLAW_GATEWAY_TOKEN=REPLACE_WITH_UNIQUE_GATEWAY_TOKEN

View File

@@ -0,0 +1,41 @@
{
"gateway": {
"mode": "local",
"port": 18789,
"bind": "lan",
"auth": { "mode": "token" },
"http": {
"endpoints": {
"chatCompletions": { "enabled": true }
}
}
},
"agents": {
"defaults": {
"workspace": "/home/node/workspace",
"model": { "primary": "zai/glm-5" }
}
},
// Z.ai is built in and uses ZAI_API_KEY.
// Ollama is configured for optional local reasoning fallback.
"models": {
"mode": "merge",
"providers": {
"ollama": {
"baseUrl": "http://10.1.1.42:11434/v1",
"api": "openai-completions",
"models": [
{
"id": "cogito",
"name": "Cogito (Local Reasoning)",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 128000,
"maxTokens": 8192
}
]
}
}
}
}

View File

@@ -0,0 +1,3 @@
OPENCLAW_CONFIG_PATH=/config/openclaw.json
ZAI_API_KEY=REPLACE_WITH_ZAI_API_KEY
OPENCLAW_GATEWAY_TOKEN=REPLACE_WITH_UNIQUE_GATEWAY_TOKEN

View File

@@ -0,0 +1,40 @@
{
"gateway": {
"mode": "local",
"port": 18789,
"bind": "lan",
"auth": { "mode": "token" },
"http": {
"endpoints": {
"chatCompletions": { "enabled": true }
}
}
},
"agents": {
"defaults": {
"workspace": "/home/node/workspace",
"model": { "primary": "ollama/cogito" }
}
},
// Operations uses local Ollama Cogito as the primary model.
"models": {
"mode": "merge",
"providers": {
"ollama": {
"baseUrl": "http://10.1.1.42:11434/v1",
"api": "openai-completions",
"models": [
{
"id": "cogito",
"name": "Cogito (Local Reasoning)",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 128000,
"maxTokens": 8192
}
]
}
}
}
}

View File

@@ -0,0 +1,3 @@
OPENCLAW_CONFIG_PATH=/config/openclaw.json
ZAI_API_KEY=REPLACE_WITH_ZAI_API_KEY
OPENCLAW_GATEWAY_TOKEN=REPLACE_WITH_UNIQUE_GATEWAY_TOKEN

View File

@@ -0,0 +1,39 @@
{
"gateway": {
"mode": "local",
"port": 18789,
"bind": "lan",
"auth": { "mode": "token" },
"http": {
"endpoints": {
"chatCompletions": { "enabled": true }
}
}
},
"agents": {
"defaults": {
"workspace": "/home/node/workspace",
"model": { "primary": "zai/glm-5" }
}
},
"models": {
"mode": "merge",
"providers": {
"ollama": {
"baseUrl": "http://10.1.1.42:11434/v1",
"api": "openai-completions",
"models": [
{
"id": "cogito",
"name": "Cogito (Local Reasoning)",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 128000,
"maxTokens": 8192
}
]
}
}
}
}

View File

@@ -0,0 +1,3 @@
OPENCLAW_CONFIG_PATH=/config/openclaw.json
ZAI_API_KEY=REPLACE_WITH_ZAI_API_KEY
OPENCLAW_GATEWAY_TOKEN=REPLACE_WITH_UNIQUE_GATEWAY_TOKEN

View File

@@ -0,0 +1,39 @@
{
"gateway": {
"mode": "local",
"port": 18789,
"bind": "lan",
"auth": { "mode": "token" },
"http": {
"endpoints": {
"chatCompletions": { "enabled": true }
}
}
},
"agents": {
"defaults": {
"workspace": "/home/node/workspace",
"model": { "primary": "zai/glm-5" }
}
},
"models": {
"mode": "merge",
"providers": {
"ollama": {
"baseUrl": "http://10.1.1.42:11434/v1",
"api": "openai-completions",
"models": [
{
"id": "cogito",
"name": "Cogito (Local Reasoning)",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 128000,
"maxTokens": 8192
}
]
}
}
}
}