Skip to content

Commit 9eb9299

Browse files
committed
chore(merge): merge feature/ai-video into develop
2 parents ec5021e + 060ef21 commit 9eb9299

46 files changed

Lines changed: 1250 additions & 211 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
![CLI](https://img.shields.io/badge/CLI-Node.js%20%2B%20TypeScript-3C873A?logo=nodedotjs&logoColor=white)
88
![Dashboard](https://img.shields.io/badge/UI-Atlas%20Dashboard-0F172A?logo=antdesign&logoColor=white)
99
![Security](https://img.shields.io/badge/Ingress-HTTPS%20Only-0F766E)
10-
![Profiles](https://img.shields.io/badge/Layers-core%20%7C%20ai--llm%20%7C%20ai--image%20%7C%20workbench-7C3AED)
10+
![Profiles](https://img.shields.io/badge/Layers-core%20%7C%20ai--agents%20%7C%20ai--llm%20%7C%20ai--image%20%7C%20ai--video%20%7C%20workbench-7C3AED)
1111
![Persistence](https://img.shields.io/badge/Persistence-Docker%20Volumes-CA8A04)
1212

1313
> 🧭 **Atlas Lab** is a localhost-first self-hosted platform made of a Node.js/TypeScript CLI, a layered Docker Compose stack, and an operational React dashboard served by the gateway.
14-
> It is designed to provide Git hosting, automation, optional local AI LLM services, optional AI image generation, browser-based development workbenches, and structured image/volume backup workflows on a single machine.
14+
> It is designed to provide Git hosting, optional automation agents, optional local AI LLM services, optional AI image and video generation, browser-based development workbenches, and structured image/volume backup workflows on a single machine.
1515
1616
---
1717

@@ -21,9 +21,11 @@ Atlas Lab is built for a practical goal: run a repeatable local engineering plat
2121

2222
### What it gives you
2323

24-
- 🧱 An always-on **core layer** with Gitea, n8n, the gateway, and Atlas Dashboard
24+
- 🧱 An always-on **core layer** with Gitea, the gateway, and Atlas Dashboard
25+
- 🤖 An optional **AI agents layer** with n8n and external runners
2526
- 🧠 An optional **AI LLM layer** with Open WebUI and Ollama
2627
- 🖼️ An optional **AI image layer** with InvokeAI and a script-managed local model set
28+
- 🎬 An optional **AI video layer** with ComfyUI and managed local video models
2729
- 🛠️ An optional **workbench layer** with browser-based Node, Python, AI, and C++ environments plus shared PostgreSQL
2830
- 🔐 HTTPS-only ingress on `localhost`
2931
- 📦 A self-contained npm package that can run without a local repository checkout
@@ -63,13 +65,15 @@ Atlas Lab is built for a practical goal: run a repeatable local engineering plat
6365

6466
## 🏗️ Architecture
6567

66-
Atlas Lab is split into **four explicit layers**:
68+
Atlas Lab is split into **six explicit layers**:
6769

6870
| Layer | Status | Includes | Purpose |
6971
| --- | --- | --- | --- |
70-
| `core` | always on | gateway, Atlas Dashboard, Gitea, Gitea DB, n8n, n8n runners | baseline platform |
72+
| `core` | always on | gateway, Atlas Dashboard, Gitea, Gitea DB | baseline platform |
73+
| `ai-agents` | optional | n8n, n8n runners, AI agents gateway | workflow automation and agent orchestration |
7174
| `ai-llm` | optional | Open WebUI, Ollama, AI LLM gateway | local LLM workflows |
7275
| `ai-image` | optional | InvokeAI, AI image gateway, managed model staging | local image generation |
76+
| `ai-video` | optional | ComfyUI, AI video gateway, managed model staging | local video generation |
7377
| `workbench` | optional | Node Forge, Python Grid, AI Reactor, C++ Foundry, shared PostgreSQL, workbench gateway | browser-based development |
7478

7579
### Why the current topology
@@ -97,7 +101,7 @@ The CLI:
97101
- runs host preflight checks
98102
- reconciles runtime state
99103
- bootstraps Gitea
100-
- bootstraps n8n
104+
- bootstraps n8n only when the `ai-agents` layer is enabled
101105
- reconciles Ollama only when the AI LLM layer is enabled
102106
- cleans up legacy runtime artifacts
103107

@@ -112,10 +116,11 @@ The only host-level TCP service exposed directly is PostgreSQL from the workbenc
112116
| --- | --- | --- | --- |
113117
| Atlas Dashboard | `core` | `https://localhost:8443/` | operational dashboard |
114118
| Gitea | `core` | `https://localhost:8444/` | Git forge, issues, reviews |
115-
| n8n | `core` | `https://localhost:8445/` | workflow automation |
119+
| n8n | `ai-agents` | `https://localhost:8445/` | workflow automation |
116120
| Open WebUI | `ai-llm` | `https://localhost:8446/` | only with `--with-ai-llm` |
117121
| Ollama | `ai-llm` | `https://localhost:8447/` | HTTPS API |
118122
| InvokeAI | `ai-image` | `https://localhost:8448/` | only with `--with-ai-image` |
123+
| ComfyUI | `ai-video` | `https://localhost:8449/` | only with `--with-ai-video` |
119124
| Node Forge | `workbench` | `https://localhost:8450/` | Node / TypeScript workspace |
120125
| Python Grid | `workbench` | `https://localhost:8451/` | Python workspace |
121126
| AI Reactor | `workbench` | `https://localhost:8452/` | AI / notebook workspace |
@@ -135,9 +140,11 @@ The only host-level TCP service exposed directly is PostgreSQL from the workbenc
135140
| Network | Type | Purpose |
136141
| --- | --- | --- |
137142
| `edge-net` | exposed | published ingress ports |
138-
| `apps-net` | internal | core application services |
143+
| `apps-net` | internal | Gitea and shared browser-facing services |
144+
| `ai-agents-net` | internal | n8n and external runners |
139145
| `ai-llm-net` | internal | Open WebUI and Ollama |
140146
| `ai-image-net` | internal | InvokeAI and image-generation runtime |
147+
| `ai-video-net` | internal | ComfyUI and video-generation runtime |
141148
| `data-net` | internal | data services and infrastructure databases |
142149
| `workbench-net` | internal | workbenches and PostgreSQL |
143150
| `workbench-host-net` | bridge | host-side PostgreSQL bind |
@@ -206,6 +213,7 @@ The AI LLM and AI image layers require:
206213
- `8446`
207214
- `8447`
208215
- `8448`
216+
- `8449`
209217
- `8450`
210218
- `8451`
211219
- `8452`
@@ -246,12 +254,12 @@ Key variables include:
246254
- `APP_VERSION`
247255
- `LAB_HTTPS_PORT`, `GITEA_HTTPS_PORT`, `N8N_HTTPS_PORT`
248256
- `OPENWEBUI_HTTPS_PORT`, `OLLAMA_HTTPS_PORT`
249-
- `INVOKEAI_HTTPS_PORT`
257+
- `INVOKEAI_HTTPS_PORT`, `COMFYUI_HTTPS_PORT`
250258
- `NODE_DEV_HTTPS_PORT`, `PYTHON_DEV_HTTPS_PORT`, `AI_DEV_HTTPS_PORT`, `CPP_DEV_HTTPS_PORT`
251259
- `POSTGRES_DEV_HOST_PORT`
252260
- `OLLAMA_CHAT_MODEL`, `OLLAMA_EMBEDDING_MODEL`, `OLLAMA_RUNTIME_MODELS`
253261
- `INVOKEAI_MODEL_REPO`, `INVOKEAI_MODEL_REVISION`, `INVOKEAI_MODEL_TITLE`
254-
- `config/models/invokeai-models.json`
262+
- `config/models/invokeai-models.json`, `config/models/comfyui-models.json`
255263
- `GITEA_ROOT_USERNAME`, `GITEA_ROOT_PASSWORD`
256264
- `N8N_ROOT_EMAIL`, `N8N_ROOT_PASSWORD`
257265
- `OPENWEBUI_ROOT_EMAIL`, `OPENWEBUI_ROOT_PASSWORD`
@@ -432,19 +440,19 @@ Atlas Lab supports backup and restore for both **Docker images** and **Docker vo
432440
- one `.tar.gz` archive for selected volumes
433441
- embedded manifest metadata
434442
- realtime progress logs during export and restore
435-
- support for `core`, `ai-llm`, `ai-image`, and `workbench` layer selection
443+
- support for `core`, `ai-agents`, `ai-llm`, `ai-image`, `ai-video`, and `workbench` layer selection
436444

437445
### Examples
438446

439447
```powershell
440-
npm run dev -- save-images --with-ai-llm --with-ai-image --with-workbench
448+
npm run dev -- save-images --with-ai-agents --with-ai-llm --with-ai-image --with-ai-video --with-workbench
441449
npm run dev -- restore-images --input .\backups\images\atlas-lab-images.tar.gz
442450
npm run dev -- down
443-
npm run dev -- save-volumes --with-ai-llm --with-ai-image --with-workbench
451+
npm run dev -- save-volumes --with-ai-agents --with-ai-llm --with-ai-image --with-ai-video --with-workbench
444452
npm run dev -- restore-volumes --input .\backups\volumes\atlas-lab-volumes.tar.gz
445453
```
446454

447-
Bootstrap is idempotent and reconciles Gitea, n8n, and optionally Ollama.
455+
Bootstrap is idempotent and reconciles Gitea, n8n when `ai-agents` is enabled, and Ollama when `ai-llm` is enabled.
448456

449457
---
450458

@@ -460,6 +468,7 @@ Bootstrap is idempotent and reconciles Gitea, n8n, and optionally Ollama.
460468
| Open WebUI | `https://localhost:8446/` | `root@openwebui.local / RootOpenWebUI!2026` |
461469
| Ollama | `https://localhost:8447/` | gateway basic auth `root / RootOllama!2026` |
462470
| InvokeAI | `https://localhost:8448/` | gateway basic auth `root / RootInvokeAI!2026` |
471+
| ComfyUI | `https://localhost:8449/` | gateway basic auth `root / RootComfyUI!2026` |
463472
| PostgreSQL host-side | `localhost:15432` | `postgres / RootPostgresDev!2026` |
464473

465474
For DBeaver and other desktop PostgreSQL clients:

apps/atlas-dashboard/src/App.tsx

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
NodeIndexOutlined,
1616
RobotOutlined,
1717
SafetyCertificateOutlined,
18-
ThunderboltOutlined
18+
ThunderboltOutlined,
19+
VideoCameraOutlined
1920
} from '@ant-design/icons';
2021
import {
2122
Alert,
@@ -70,13 +71,19 @@ const iconMap: Record<DashboardIconKey, DashboardIconComponent> = {
7071
secure: LockOutlined,
7172
spark: ThunderboltOutlined,
7273
terminal: CodeOutlined,
74+
video: VideoCameraOutlined,
7375
workflow: BranchesOutlined
7476
};
7577

7678
const toneStyles: Record<
7779
DashboardTone,
7880
{ accent: string; border: string; soft: string }
7981
> = {
82+
agents: {
83+
accent: atlasDashboardPalette.signal,
84+
border: 'rgba(77, 163, 255, 0.28)',
85+
soft: 'rgba(77, 163, 255, 0.14)'
86+
},
8087
ai: {
8188
accent: atlasDashboardPalette.ai,
8289
border: 'rgba(214, 138, 72, 0.28)',
@@ -92,6 +99,11 @@ const toneStyles: Record<
9299
border: 'rgba(200, 92, 255, 0.28)',
93100
soft: 'rgba(200, 92, 255, 0.14)'
94101
},
102+
video: {
103+
accent: atlasDashboardPalette.image,
104+
border: 'rgba(200, 92, 255, 0.28)',
105+
soft: 'rgba(200, 92, 255, 0.14)'
106+
},
95107
neutral: {
96108
accent: atlasDashboardPalette.signal,
97109
border: 'rgba(91, 146, 200, 0.24)',
@@ -195,8 +207,10 @@ export default function App() {
195207
</Col>
196208
<Col xs={24} xl={8} style={{ display: 'flex' }}>
197209
<LayerRail
210+
agentsLayer={dashboard.agentsLayer}
198211
aiLayer={dashboard.aiLayer}
199212
imageLayer={dashboard.imageLayer}
213+
videoLayer={dashboard.videoLayer}
200214
workbenchLayer={dashboard.workbenchLayer}
201215
/>
202216
</Col>
@@ -247,6 +261,30 @@ export default function App() {
247261
))}
248262
</Row>
249263

264+
<SectionBand
265+
body={t(
266+
dashboard.agentsLayer.enabled
267+
? 'sections.agentsBodyEnabled'
268+
: 'sections.agentsBodyDisabled'
269+
)}
270+
kicker={t('sections.agentsKicker')}
271+
title={t('sections.agentsTitle')}
272+
/>
273+
<LayerStateCard layer={dashboard.agentsLayer} />
274+
{dashboard.agentsLayer.enabled ? (
275+
<Row gutter={[24, 24]}>
276+
{dashboard.agentServices.map((service) => (
277+
<Col xs={24} xl={12} key={service.id}>
278+
<OperationalCard
279+
item={service}
280+
primaryAction={service.action}
281+
tone={service.tone}
282+
/>
283+
</Col>
284+
))}
285+
</Row>
286+
) : null}
287+
250288
<SectionBand
251289
body={t(
252290
dashboard.imageLayer.enabled
@@ -297,6 +335,32 @@ export default function App() {
297335
</Row>
298336
) : null}
299337

338+
<SectionBand
339+
body={t(
340+
dashboard.videoLayer.enabled
341+
? 'sections.videoBodyEnabled'
342+
: 'sections.videoBodyDisabled'
343+
)}
344+
kicker={t('sections.videoKicker')}
345+
title={t('sections.videoTitle')}
346+
/>
347+
<LayerStateCard layer={dashboard.videoLayer} />
348+
{dashboard.videoLayer.enabled ? (
349+
<Row gutter={[24, 24]}>
350+
{dashboard.videoServices.map((service) => (
351+
<Col xs={24} xl={12} key={service.id}>
352+
<OperationalCard
353+
briefing={service.briefing}
354+
item={service}
355+
onOpenBriefing={setActiveBriefing}
356+
primaryAction={service.action}
357+
tone={service.tone}
358+
/>
359+
</Col>
360+
))}
361+
</Row>
362+
) : null}
363+
300364
<SectionBand
301365
body={t(
302366
dashboard.workbenchLayer.enabled
@@ -653,12 +717,16 @@ function StatsRail({
653717
}
654718

655719
function LayerRail({
720+
agentsLayer,
656721
aiLayer,
657722
imageLayer,
723+
videoLayer,
658724
workbenchLayer
659725
}: {
726+
agentsLayer: { enabled: boolean; summary: string; tone: DashboardTone };
660727
aiLayer: { enabled: boolean; summary: string; tone: DashboardTone };
661728
imageLayer: { enabled: boolean; summary: string; tone: DashboardTone };
729+
videoLayer: { enabled: boolean; summary: string; tone: DashboardTone };
662730
workbenchLayer: { enabled: boolean; summary: string; tone: DashboardTone };
663731
}) {
664732
const { t } = useTranslation();
@@ -674,6 +742,12 @@ function LayerRail({
674742
title={t('cards.tones.core')}
675743
tone="core"
676744
/>
745+
<LayerSummaryTile
746+
enabled={agentsLayer.enabled}
747+
summary={agentsLayer.summary}
748+
title={t('cards.tones.agents')}
749+
tone={agentsLayer.tone}
750+
/>
677751
<LayerSummaryTile
678752
enabled={aiLayer.enabled}
679753
summary={aiLayer.summary}
@@ -686,6 +760,12 @@ function LayerRail({
686760
title={t('cards.tones.image')}
687761
tone={imageLayer.tone}
688762
/>
763+
<LayerSummaryTile
764+
enabled={videoLayer.enabled}
765+
summary={videoLayer.summary}
766+
title={t('cards.tones.video')}
767+
tone={videoLayer.tone}
768+
/>
689769
<LayerSummaryTile
690770
enabled={workbenchLayer.enabled}
691771
summary={workbenchLayer.summary}
@@ -797,8 +877,12 @@ function LayerSummaryTile({
797877
const { t } = useTranslation();
798878
const palette = toneStyles[tone];
799879
const IconGlyph =
800-
tone === 'ai'
880+
tone === 'agents'
881+
? BranchesOutlined
882+
: tone === 'ai'
801883
? ApiOutlined
884+
: tone === 'video'
885+
? VideoCameraOutlined
802886
: tone === 'image'
803887
? PictureOutlined
804888
: tone === 'workbench'
@@ -964,8 +1048,12 @@ function LayerStateCard({
9641048
const { t } = useTranslation();
9651049
const palette = toneStyles[layer.tone];
9661050
const IconGlyph =
967-
layer.tone === 'ai'
1051+
layer.tone === 'agents'
1052+
? BranchesOutlined
1053+
: layer.tone === 'ai'
9681054
? ApiOutlined
1055+
: layer.tone === 'video'
1056+
? VideoCameraOutlined
9691057
: layer.tone === 'image'
9701058
? PictureOutlined
9711059
: CodeOutlined;
@@ -1337,9 +1425,13 @@ function SignalPill({
13371425
const capsuleBg =
13381426
tone === 'core'
13391427
? 'rgba(31, 159, 141, 0.18)'
1428+
: tone === 'agents'
1429+
? 'rgba(77, 163, 255, 0.18)'
13401430
: tone === 'ai'
13411431
? 'rgba(214, 138, 72, 0.18)'
1342-
: tone === 'image'
1432+
: tone === 'image'
1433+
? 'rgba(200, 92, 255, 0.18)'
1434+
: tone === 'video'
13431435
? 'rgba(200, 92, 255, 0.18)'
13441436
: tone === 'workbench'
13451437
? 'rgba(90, 143, 201, 0.18)'

0 commit comments

Comments
 (0)