-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement web UI dashboard with Vue 3 + PrimeVue + Tailwind CSS #334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1c1d0f2
c4d5d06
4cb2cd3
5f94dcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,10 +99,50 @@ jobs: | |
| name: test-results-${{ matrix.python-version }} | ||
| path: junit.xml | ||
|
|
||
| dashboard-lint: | ||
| name: Dashboard Lint & Type Check | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| with: | ||
| persist-credentials: false | ||
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
| cache-dependency-path: web/package-lock.json | ||
| - run: npm ci | ||
| working-directory: web | ||
| - run: npm run lint | ||
| working-directory: web | ||
| - run: npm run type-check | ||
| working-directory: web | ||
|
|
||
| dashboard-test: | ||
| name: Dashboard Test | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| with: | ||
| persist-credentials: false | ||
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
| cache-dependency-path: web/package-lock.json | ||
| - run: npm ci | ||
| working-directory: web | ||
| - run: npm run test -- --coverage | ||
| working-directory: web | ||
|
|
||
|
Comment on lines
+123
to
+141
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider adding coverage artifact upload for dashboard tests. The backend test job uploads coverage to Codecov, but dashboard tests run coverage without uploading. For consistency and visibility, consider adding coverage artifact upload: 💡 Optional: Add coverage upload - name: Upload coverage to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: dashboard
fail_ci_if_error: false🤖 Prompt for AI Agents |
||
| ci-pass: | ||
| name: CI Pass | ||
| if: always() | ||
| needs: [lint, type-check, test] | ||
| needs: [lint, type-check, test, dashboard-lint, dashboard-test] | ||
| runs-on: ubuntu-latest | ||
| permissions: {} | ||
| steps: | ||
|
|
@@ -111,11 +151,15 @@ jobs: | |
| LINT_RESULT: ${{ needs.lint.result }} | ||
| TYPE_CHECK_RESULT: ${{ needs.type-check.result }} | ||
| TEST_RESULT: ${{ needs.test.result }} | ||
| DASHBOARD_LINT_RESULT: ${{ needs.dashboard-lint.result }} | ||
| DASHBOARD_TEST_RESULT: ${{ needs.dashboard-test.result }} | ||
| run: | | ||
| if [[ "$LINT_RESULT" != "success" || \ | ||
| "$TYPE_CHECK_RESULT" != "success" || \ | ||
| "$TEST_RESULT" != "success" ]]; then | ||
| echo "CI failed: lint=$LINT_RESULT, type-check=$TYPE_CHECK_RESULT, test=$TEST_RESULT" | ||
| "$TEST_RESULT" != "success" || \ | ||
| "$DASHBOARD_LINT_RESULT" != "success" || \ | ||
| "$DASHBOARD_TEST_RESULT" != "success" ]]; then | ||
| echo "CI failed: lint=$LINT_RESULT, type-check=$TYPE_CHECK_RESULT, test=$TEST_RESULT, dashboard-lint=$DASHBOARD_LINT_RESULT, dashboard-test=$DASHBOARD_TEST_RESULT" | ||
| exit 1 | ||
| fi | ||
| echo "All CI checks passed" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ htmlcov/ | |
| coverage.xml | ||
| .coverage | ||
| .coverage.* | ||
| web/coverage/ | ||
|
|
||
| # Environment variables | ||
| .env | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ | |
| - **What**: Framework for building synthetic organizations — autonomous AI agents orchestrated as a virtual company | ||
| - **Python**: 3.14+ (PEP 649 native lazy annotations) | ||
| - **License**: BUSL-1.1 (converts to Apache 2.0 on 2030-02-27) | ||
| - **Layout**: `src/ai_company/` (src layout), `tests/` (unit/integration/e2e) | ||
| - **Layout**: `src/ai_company/` (src layout), `tests/` (unit/integration/e2e), `web/` (Vue 3 dashboard) | ||
| - **Design**: [DESIGN_SPEC.md](DESIGN_SPEC.md) (pointer to `docs/design/` pages) | ||
|
|
||
| ## Design Spec (MANDATORY) | ||
|
|
@@ -43,6 +43,17 @@ uv run zensical build # build docs (output: _site/docs/) | |
| uv run zensical serve # local docs preview (http://127.0.0.1:8000) | ||
| ``` | ||
|
|
||
| ### Web Dashboard | ||
|
|
||
| ```bash | ||
| npm --prefix web install # install frontend deps | ||
| npm --prefix web run dev # dev server (http://localhost:5173) | ||
| npm --prefix web run build # production build | ||
| npm --prefix web run lint # ESLint | ||
| npm --prefix web run type-check # vue-tsc type checking | ||
| npm --prefix web run test # Vitest unit tests | ||
| ``` | ||
|
Comment on lines
+46
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix command-style contradiction in the Web Dashboard quick commands. Lines 49-54 use Suggested doc fix-cd web && npm install # install frontend deps
-cd web && npm run dev # dev server (http://localhost:5173)
-cd web && npm run build # production build
-cd web && npm run lint # ESLint
-cd web && npm run type-check # vue-tsc type checking
-cd web && npm run test # Vitest unit tests
+npm --prefix web install # install frontend deps
+npm --prefix web run dev # dev server (http://localhost:5173)
+npm --prefix web run build # production build
+npm --prefix web run lint # ESLint
+npm --prefix web run type-check # vue-tsc type checking
+npm --prefix web run test # Vitest unit tests🤖 Prompt for AI Agents |
||
|
|
||
| ## Documentation | ||
|
|
||
| - **Docs source**: `docs/` (Markdown, built with Zensical) | ||
|
|
@@ -75,7 +86,7 @@ curl http://localhost:3000/api/v1/health # backend (via web proxy) | |
| ``` | ||
|
|
||
| - **Backend**: 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened | ||
| - **Web**: `nginxinc/nginx-unprivileged`, SPA routing, API/WebSocket proxy to backend | ||
| - **Web**: `nginxinc/nginx-unprivileged`, Vue 3 SPA (PrimeVue + Tailwind CSS), SPA routing, API/WebSocket proxy to backend | ||
| - **Config**: all Docker files in `docker/` — Dockerfiles, compose, `.env.example` | ||
| - **CI**: `.github/workflows/docker.yml` — build → scan → push to GHCR + cosign sign (images only pushed after Trivy/Grype scans pass) | ||
| - **Build context**: single root `.dockerignore` (both images build with `context: .`) | ||
|
|
@@ -101,6 +112,18 @@ src/ai_company/ | |
| security/ # SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume) | ||
| templates/ # Pre-built company templates, personality presets, and builder | ||
| tools/ # Tool registry, built-in tools (file_system/, git, sandbox/, code_runner), MCP bridge (mcp/), role-based access | ||
|
|
||
| web/ # Vue 3 + PrimeVue + Tailwind CSS dashboard | ||
| src/ | ||
| api/ # Axios client, endpoint modules, TypeScript types (mirrors backend Pydantic models) | ||
| components/ # Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/) | ||
| composables/ # Reusable composition functions (useAuth, usePolling, useOptimisticUpdate) | ||
| router/ # Vue Router config with auth guards | ||
| stores/ # Pinia stores (auth, agents, tasks, budget, messages, approvals, websocket, analytics, company, providers) | ||
| styles/ # Global CSS and PrimeVue theme configuration | ||
| utils/ # Constants, formatters, error helpers | ||
| views/ # Page-level components (Dashboard, TaskBoard, AgentProfiles, BudgetPanel, etc.) | ||
| __tests__/ # Vitest unit tests (organized by feature) | ||
| ``` | ||
|
|
||
| ## Shell Usage | ||
|
|
@@ -206,3 +229,4 @@ src/ai_company/ | |
| - **Pinned**: all versions use `==` in `pyproject.toml` | ||
| - **Groups**: `test` (pytest + plugins), `dev` (includes test + ruff, mypy, pre-commit, commitizen) | ||
| - **Install**: `uv sync` installs everything (dev group is default) | ||
| - **Web dashboard**: Node.js 20+, dependencies in `web/package.json` (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,19 @@ | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| # ============================================================================= | ||
| # SynthOrg Web — Non-root nginx container (CIS hardening applied via compose.yml) | ||
| # SynthOrg Web — Multi-stage build: Node builder → nginx runtime | ||
| # CIS hardening applied via compose.yml (no-new-privileges, drop caps, etc.) | ||
| # ============================================================================= | ||
|
|
||
| # Stage 1: Build Vue dashboard | ||
| FROM node:22-alpine AS builder | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -nP '^\s*FROM\s+(?!\S+@sha256:)\S+' docker --glob '**/Dockerfile'Repository: Aureliolo/synthorg Length of output: 225 🏁 Script executed: cat docker/web/DockerfileRepository: Aureliolo/synthorg Length of output: 1349 Pin the builder base image by digest to match the runtime stage. Line 9 uses a mutable tag ( Suggested fix-FROM node:22-alpine AS builder
+FROM node:22-alpine@sha256:<resolved_digest> AS builder🤖 Prompt for AI Agents |
||
| WORKDIR /app | ||
| COPY web/package.json web/package-lock.json ./ | ||
| RUN npm ci | ||
| COPY web/ ./ | ||
| RUN npm run build | ||
|
|
||
| # Stage 2: Serve with nginx | ||
| FROM nginxinc/nginx-unprivileged:1.29.5-alpine@sha256:aec540f08f99df3c830549d5dd7bfaf63e01cbbb499e37400c5af9f8e8554e9f | ||
|
|
||
| LABEL org.opencontainers.image.title="synthorg-web" \ | ||
|
|
@@ -14,7 +24,7 @@ LABEL org.opencontainers.image.title="synthorg-web" \ | |
| org.opencontainers.image.vendor="Aureliolo" | ||
|
|
||
| COPY web/nginx.conf /etc/nginx/conf.d/default.conf | ||
| COPY web/index.html web/style.css web/app.js /usr/share/nginx/html/ | ||
| COPY --from=builder /app/dist/ /usr/share/nginx/html/ | ||
|
|
||
| EXPOSE 8080 | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,10 +17,10 @@ docker compose -f docker/compose.yml up -d | |
|
|
||
| The web dashboard is at [http://localhost:3000](http://localhost:3000). | ||
|
|
||
| Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup and templates will be configurable through the dashboard once available. | ||
| Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup is done via the dashboard. Template configuration through the UI is planned for a future release. | ||
|
|
||
| !!! danger "Work in Progress" | ||
| SynthOrg is under active development. The web dashboard, templates, and many features described here are **not yet available**. Check the [GitHub repository](https://github.com/Aureliolo/synthorg) for current status. | ||
| !!! info "Active Development" | ||
| SynthOrg is under active development. The web dashboard is available for monitoring and managing the organization. Templates and some features described here may evolve. Check the [GitHub repository](https://github.com/Aureliolo/synthorg) for current status. | ||
|
Comment on lines
+22
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolve contradictory dashboard availability messaging. Line 23 says the dashboard is available, but Line 20 still says setup/templates are configurable “once available.” Please align those statements to avoid confusing users. 📝 Suggested doc fix-Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup and templates will be configurable through the dashboard once available.
+Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup is available through the dashboard, while template options may continue to evolve.🤖 Prompt for AI Agents |
||
|
|
||
| ## Templates | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # API base URL — empty for production (relative paths), set for dev if not using Vite proxy | ||
| VITE_API_BASE_URL= |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| /// <reference types="vite/client" /> | ||
|
|
||
| declare module '*.vue' { | ||
| import type { DefineComponent } from 'vue' | ||
| const component: DefineComponent<object, object, unknown> | ||
| export default component | ||
| } | ||
|
|
||
| interface ImportMetaEnv { | ||
| readonly VITE_API_BASE_URL: string | ||
| } | ||
|
|
||
| interface ImportMeta { | ||
| readonly env: ImportMetaEnv | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import pluginVue from 'eslint-plugin-vue' | ||
| import pluginSecurity from 'eslint-plugin-security' | ||
| import tsParser from '@typescript-eslint/parser' | ||
|
|
||
| export default [ | ||
| ...pluginVue.configs['flat/essential'], | ||
| pluginSecurity.configs.recommended, | ||
| { | ||
| files: ['**/*.vue'], | ||
| languageOptions: { | ||
| parserOptions: { | ||
| parser: tsParser, | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| files: ['**/*.ts'], | ||
| languageOptions: { | ||
| parser: tsParser, | ||
| }, | ||
| }, | ||
| { | ||
| rules: { | ||
| 'vue/multi-word-component-names': 'off', | ||
| 'vue/no-v-html': 'warn', | ||
| }, | ||
| }, | ||
| { | ||
| ignores: ['dist/'], | ||
| }, | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,20 +1,12 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <!DOCTYPE html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <html lang="en"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <meta charset="UTF-8"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <title>SynthOrg</title> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <link rel="stylesheet" href="/style.css"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="container"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h1>SynthOrg</h1> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p class="subtitle">Dashboard — Coming Soon</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div id="status" class="status status-checking" aria-live="polite"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="status-dot"></span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span id="status-text">Checking backend...</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script src="/app.js"></script> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <html lang="en" class="dark"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <meta charset="UTF-8" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <title>SynthOrg Dashboard</title> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <body class="bg-slate-950 text-slate-200 antialiased"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div id="app"></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script type="module" src="/src/main.ts"></script> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
1
to
12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider adding noscript fallback and favicon. The SPA bootstrap is clean, but consider adding a ♻️ Optional enhancements <head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>SynthOrg Dashboard</title>
</head>
<body class="bg-slate-950 text-slate-200 antialiased">
+ <noscript>
+ <div style="padding: 2rem; text-align: center;">
+ JavaScript is required to use this dashboard.
+ </div>
+ </noscript>
<div id="app"></div>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Aureliolo/synthorg
Length of output: 8996
🏁 Script executed:
Repository: Aureliolo/synthorg
Length of output: 663
🏁 Script executed:
sed -n '130,150p' .github/workflows/ci.ymlRepository: Aureliolo/synthorg
Length of output: 644
🏁 Script executed:
cat -n .github/workflows/ci.yml | tail -50Repository: Aureliolo/synthorg
Length of output: 2190
Add coverage threshold enforcement to block builds below 80%.
The test command runs with
--coveragebut vitest.config.ts lacks threshold configuration to enforce the 80% minimum. Configure coverage thresholds in the vitest config or add a validation step to fail the build when coverage falls below 80% for lines, statements, functions, and branches.🤖 Prompt for AI Agents