Problem
When the dashboard API enables an extension (_activate_service in routers/extensions.py), it renames compose.yaml.disabled → compose.yaml but does not invalidate .compose-flags.
The CLI's `get_compose_flags` reads .compose-flags from cache (fast path in dream-cli:295) — which was written at install time and only regenerated when the CLI itself toggles extensions (cmd_enable/cmd_disable via _regenerate_compose_flags).
Result: after API-driven enable, any subsequent CLI command (`dream start`, `dream restart`, `dream stop`) operates on a stale stack that doesn't include the newly enabled services.
Reproduction
- Install DreamServer, some extensions disabled (e.g., tts, whisper)
- From the dashboard UI, click Enable on tts or apply a template containing tts
- Dashboard API calls
_activate_service("tts") which renames extensions/services/tts/compose.yaml.disabled → compose.yaml
- Run `dream start` from the terminal
- Bug: tts is not started. Docker reports dream-tts as an orphan container. The resolved compose stack doesn't include the tts compose file.
- Root cause:
.compose-flags still contains the old flag list from install time, without extensions/services/tts/compose.yaml
Impact
- PR 2 (service templates) directly triggers this: template apply renames compose files but doesn't update the cache, so the required follow-up `dream start` misses the newly enabled services.
- Any API-driven extension toggle silently breaks CLI operations until the user manually removes `.compose-flags` or `dream disable && dream enable` the same service.
Fix Options
Option A: Invalidate cache from API
Have _activate_service (and any other API path that toggles compose files) remove or regenerate .compose-flags after the rename. Simplest fix:
```python
import os
compose_flags = INSTALL_DIR / ".compose-flags"
if compose_flags.exists():
compose_flags.unlink()
```
This falls through to dynamic resolution on next CLI invocation.
Option B: Drop the cache entirely
.compose-flags was added as an install-time fast path. The resolver is fast enough at runtime that the cache may not be worth the cache-invalidation complexity. Remove get_compose_flags fast path, always resolve dynamically.
Option C: Use mtime-based cache invalidation
Compare the cache file mtime against all extensions/services/*/compose.yaml* mtimes. Regenerate if any extension compose file is newer.
Recommended
Option A — it's minimal and correct. The dashboard API already imports INSTALL_DIR from config. Add cache invalidation to both _activate_service (enable path) and disable_extension (disable path).
Files Affected
- `dream-server/extensions/services/dashboard-api/routers/extensions.py` — `_activate_service`, `disable_extension`
- `dream-server/extensions/services/dashboard-api/routers/templates.py` — indirectly via `_activate_service` calls
- `dream-server/dream-cli` — already correct (`_regenerate_compose_flags` is called from CLI enable/disable paths)
Problem
When the dashboard API enables an extension (
_activate_serviceinrouters/extensions.py), it renamescompose.yaml.disabled→compose.yamlbut does not invalidate.compose-flags.The CLI's `get_compose_flags` reads
.compose-flagsfrom cache (fast path indream-cli:295) — which was written at install time and only regenerated when the CLI itself toggles extensions (cmd_enable/cmd_disablevia_regenerate_compose_flags).Result: after API-driven enable, any subsequent CLI command (`dream start`, `dream restart`, `dream stop`) operates on a stale stack that doesn't include the newly enabled services.
Reproduction
_activate_service("tts")which renamesextensions/services/tts/compose.yaml.disabled→compose.yaml.compose-flagsstill contains the old flag list from install time, withoutextensions/services/tts/compose.yamlImpact
Fix Options
Option A: Invalidate cache from API
Have
_activate_service(and any other API path that toggles compose files) remove or regenerate.compose-flagsafter the rename. Simplest fix:```python
import os
compose_flags = INSTALL_DIR / ".compose-flags"
if compose_flags.exists():
compose_flags.unlink()
```
This falls through to dynamic resolution on next CLI invocation.
Option B: Drop the cache entirely
.compose-flagswas added as an install-time fast path. The resolver is fast enough at runtime that the cache may not be worth the cache-invalidation complexity. Removeget_compose_flagsfast path, always resolve dynamically.Option C: Use mtime-based cache invalidation
Compare the cache file mtime against all
extensions/services/*/compose.yaml*mtimes. Regenerate if any extension compose file is newer.Recommended
Option A — it's minimal and correct. The dashboard API already imports
INSTALL_DIRfrom config. Add cache invalidation to both_activate_service(enable path) anddisable_extension(disable path).Files Affected