diff --git a/.env.example b/.env.example index 6cdc3a0..1a62ef7 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,8 @@ CHAT_API_URL=http://127.0.0.1:8000 # The backend LLM request has a 60-second deadline. CHAT_API_TIMEOUT_MS=70000 + +# Optional, server-only CrewAI service. Leave unset until it is available. +# CREWAI_API_URL=http://127.0.0.1:8001 +# Flows may take longer than chat. A timeout does not cancel a running flow. +CREWAI_API_TIMEOUT_MS=120000 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fc5878e..d0e352d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,8 +28,9 @@ jobs: - run: npm run format:check - run: npm run build - run: npm audit --audit-level=high - - run: npx playwright install --with-deps chromium + - run: npx playwright install --with-deps - run: npm run test:e2e + - run: npm run test:integration:automation - uses: actions/upload-artifact@v4 if: failure() with: diff --git a/AGENTS.md b/AGENTS.md index d7a9fa0..25006fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,7 @@ the backend limitations and the verification matrix. - `src/lib/api/`: backend schemas and one browser client. Validate JSON at this boundary. - `src/lib/async/`: shared async-state decisions. A failed request never becomes empty or zero. - `src/app/api/backend/`: fixed-upstream, allowlisted proxy; no arbitrary URLs, cookies or credential forwarding. +- `src/app/api/automation/`: separate fixed CrewAI upstream using the same transport; retain independent route/body validation. - LLM calls, storage, model/provider selection and secrets belong to FastAPI. ## Visual and interaction rules @@ -33,6 +34,7 @@ the backend limitations and the verification matrix. - Named component exports and explicit props; use `cn`, composition and primitive variants. - Backend errors use FastAPI `detail`, including 422 arrays. Validate the actual response contract. - No automatic retry of POST: the backend has no idempotency contract. +- Automation runs require fresh explicit acknowledgement after parameter edits and each submission. Timeout/navigation does not cancel upstream work. Never run real flows as an implementation smoke test. - No fake streaming, persistent-chat claims, model settings, authentication or writable behavior settings. Check the actual backend contract before adding a control. diff --git a/README.md b/README.md index b1c133f..29a7fa0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Отдельный UI для [app-poly-gitops-fastapi](https://github.com/justgithubaccount/app-poly-gitops-fastapi): проекты, быстрый чат, чат проекта, просмотр истории запросов и диагностика API. +Раздел «Автоматизация» подключает отдельный +[CrewAI backend](https://github.com/justgithubaccount/app-poly-gitops-crewai). Семантические дизайн-токены, общие компоненты, light/dark/system, адаптивная раскладка и явные состояния данных. @@ -22,6 +24,35 @@ npm run dev ограниченный `/api/backend/...` proxy. `CHAT_API_TIMEOUT_MS` по умолчанию 70000. LLM-провайдер, URL модели и ключ настраиваются в FastAPI, а не в браузере. +## Автоматизация / CrewAI + +На `/automation` доступны список flows и агентов, проверка подключения, +ручной запуск выбранного flow или серверного варианта по умолчанию. +Параметры: namespace, необязательное имя приложения и JSON-контекст. +Результат содержит итоговый ответ, шаги с агентами/задачами и длительностью, +метаданные и trace ID, если он пришёл от сервиса. + +Настройте **на сервере UI**, когда CrewAI запущен: + +```dotenv +CREWAI_API_URL=http://127.0.0.1:8001 +CREWAI_API_TIMEOUT_MS=120000 +``` + +URL не попадает в браузер; используется ограниченный `/api/automation/...` +proxy. Если CrewAI не подключён, интерфейс покажет ошибку подключения, +а проекты и чат продолжат работать независимо. + +Каждый запуск требует подтверждения: flow может обращаться к платной модели, +менять инфраструктуру или создавать issues. Подтверждение сбрасывается при +изменении параметров и после отправки. Контекст не может переопределять +`namespace` и `app_name`; секреты остаются в CrewAI, не в форме. + +API синхронный: шаги приходят после завершения. Истории запусков, живого +прогресса и отмены в нём нет. Уход со страницы или таймаут UI не отменяют +серверное выполнение; автоматического повтора POST нет. Результат на экране +не сохраняется после ухода/перезагрузки. Дедлайн proxy — от 1000 до 300000 мс. + ## Проверки ```bash @@ -30,6 +61,7 @@ npm run format:check npm run build npx playwright install chromium npm run test:e2e # браузерные тесты с явно заданными API fixtures +npm run test:integration:automation # настоящий proxy + отдельный loopback HTTP fixture ``` Browser-тесты запускают production build на 3100. Бэкенд и провайдер для них @@ -56,7 +88,7 @@ FastAPI и proxy, но подменяет только провайдера, п надёжная классификация таких ошибок требует изменения API. Настройки UI сейчас — тема и диагностика. API изменения модели/провайдера, -streaming, отдельные диалоги, auth и действия CrewAI не добавлены этим setup. +streaming, отдельные диалоги и auth не добавлены этим setup. `/health` проверяет доступность сервиса, а не провайдера модели. ## Проектные правила @@ -68,3 +100,5 @@ streaming, отдельные диалоги, auth и действия CrewAI н Репозитории backend, CrewAI, Helm, GitOps и provisioning остаются отдельными частями стека. Этот репозиторий не поднимает кластер и не меняет их настройки. +Публичность исходников не означает готовность публичного runtime: у текущих +сервисов нет пользовательской авторизации. Локальный UI слушает loopback. diff --git a/docs/automation-validation.md b/docs/automation-validation.md new file mode 100644 index 0000000..3d59e0f --- /dev/null +++ b/docs/automation-validation.md @@ -0,0 +1,48 @@ +# Automation validation — 2026-10-29 + +## Scope and evidence + +UI integration of the existing CrewAI HTTP contract. Only the UI repository +changed; no real CrewAI flow, cluster operation or paid LLM call was executed. + +| Check | Result | +| --------------------------------------------------------------- | -------------------------------------------------------------------- | +| Clean pinned `npm ci` | PASS; existing baseline 11 unit tests pass | +| `npm run check` | PASS: strict TypeScript, zero-warning ESLint, 21 unit/contract tests | +| `npm run build` | PASS: production routes including `/automation` and its proxy | +| `npm run format:check` | PASS | +| `npm audit --audit-level=high` | PASS: zero vulnerabilities | +| `PLAYWRIGHT_CHANNEL=chrome npm run test:e2e` | PASS: 19 scenarios, no retries | +| `PLAYWRIGHT_CHANNEL=chrome npm run test:integration:automation` | PASS: real Next proxy with an explicit loopback HTTP fixture | + +Tests were observed failing before implementation: the new contract modules +and the Automation workspace were missing on the baseline. + +## Covered behavior + +- Independent flows/agents/health discovery; error versus empty; stale catalog + remains visible after failed refresh; missing CrewAI does not break projects. +- Selected-flow and server-default execution; namespace/app name/JSON context; + reserved target overrides and malformed/non-object JSON rejected. +- Acknowledgement is mandatory, resets after input changes/submission and does + not automatically re-arm after failures. Pending prevents duplicate submits. +- Returned final answer, step output, agents/tasks, duration and trace metadata. + No fake live steps, run persistence or cancellation claim. +- Timeout preserves form parameters, warns that execution may continue and + never automatically repeats POST. +- Real Next route forwards only validated body to the fixed destination, does + not forward browser cookies, and rejects cross-origin/target-override writes. +- Desktop/mobile screenshots were opened and inspected at 1440px and 390px. + Long names, output and metadata fit at 320/390/1440px; navigation wraps. + +Local browser verification uses installed Chrome because the Browser plugin is +not available. CI installs Playwright Chromium. Fixture browser evidence proves +the UI contract, while the separate loopback check additionally proves the actual +proxy hop; neither proves real CrewAI execution or cluster/LLM readiness. + +## Preview and runtime boundary + +The local preview can show the new workspace with CrewAI unconfigured. It must +show a real connection error and disabled execution, never substitute fixture +data. To connect an actual service, set server-only `CREWAI_API_URL` and configure +that service's keys/tools/access independently. No cloud deployment is included. diff --git a/docs/superpowers/plans/2026-10-29-automation.md b/docs/superpowers/plans/2026-10-29-automation.md new file mode 100644 index 0000000..5d94443 --- /dev/null +++ b/docs/superpowers/plans/2026-10-29-automation.md @@ -0,0 +1,74 @@ +# Automation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Expose the existing CrewAI API through the shared UI. + +**Architecture:** Separate `/automation` feature and `/api/automation` proxy. +Reuse shared transport, error validation and semantic components; preserve Chat API behavior. + +**Tech Stack:** Existing Next.js, React, TypeScript, Zod, SWR, Vitest and Playwright; no new dependencies. + +**Spec:** `docs/superpowers/specs/2026-10-29-automation-design.md` + +## Global Constraints + +- Mutation target: `justgithubaccount/app-poly-gitops-ui` only; delivery through a CI-verified PR, no merge/deployment. +- No backend mutation, real flow run, paid provider call, browser credential forwarding or proprietary references. +- Manual confirmed execution only; timeout never means cancellation; results are not persistent history. +- `CREWAI_API_URL` is server-only and unset by default; deadline defaults to 120000ms with range 1000–300000ms. +- Preserve existing Chat API behavior; no new dependencies; responsive at 320/390/1440px. + +## Review Focus + +- Context overriding visible namespace/app name must be rejected (Task 1). +- A timeout may leave upstream work running; never auto-retry or imply cancellation (Tasks 1/2). +- Missing/unavailable CrewAI must not disable existing chat/projects (Tasks 1/2). +- Long flow names, outputs and metadata must not overflow mobile (Task 2). +- Stale catalog refresh must not clear valid data or masquerade as empty (Task 2). + +### Task 1: Validated service boundary + +**Files:** + +- Create: `src/lib/api/automation-schemas.ts`, `automation-proxy.ts`, `upstream-proxy.ts` and boundary tests. +- Modify: `src/lib/api/client.ts`, `proxy.ts`, `.env.example`. +- Create: `src/app/api/automation/[...path]/route.ts`. + +**Interfaces:** + +- Produces: `automationRequest(path, schema, init?)`, `flowsSchema`, `agentsSchema`, `runRequestSchema`, `runResponseSchema`, `parseRunContext(text)`. +- Produces: `automationPath(method, segments): string | null`, `proxyAutomationRequest(request, segments, fetcher?, env?): Promise`. +- Preserves: `apiRequest`, `backendPath`, `proxyBackendRequest` and their existing signatures/defaults. + +- [ ] Write failing tests for exact routes, JSON/target validation, response validation, fixed destination, no forwarded credentials, missing config, origin guard, timeout/no retry and separate service client paths. +- [ ] Run `npm test`; expected new tests fail on missing boundary behavior. +- [ ] Implement strict schemas, shared proxy/client transport and independent automation route/configuration. +- [ ] Run `npm test`; expected all tests pass, including existing Chat API guards. +- [ ] Commit the validated boundary. + +### Task 2: Automation workspace + +**Files:** + +- Create: `src/features/automation/{automation-workspace,automation-catalog,run-form,run-result}.tsx`. +- Create: `src/app/automation/page.tsx`, `tests/browser/automation.spec.ts`. +- Modify: shell/icons, existing responsive browser test, README, project instructions and foundation documentation. + +**Interfaces:** + +- Consumes: Task 1 schemas/client and shared `resolveAsyncView`, layout/data-state components. +- Produces: `AutomationWorkspace`, independent catalog regions, validated manual form and `RunResult`. + +- [ ] Write failing browser tests for discovery/empty/error states, acknowledgement, named/default run, JSON validation, pending/result/error handling, preserved drafts, stale catalog, no automatic POST retry, and long content at mobile widths. +- [ ] Run the new browser test against a clean baseline server; expected missing navigation/workspace assertions fail. +- [ ] Implement the workspace, navigation and documentation. Use native labelled select/checkbox inside existing shared form patterns; no unneeded primitive dependency. +- [ ] Run check/build/browser suite and formatting/audit; expected all pass without regressions. +- [ ] Exercise the real proxy against an explicit local HTTP fixture, inspect desktop/mobile rendering and document the evidence limits. +- [ ] Commit the workspace and verification record. + +### Delivery + +- [ ] Review the whole diff and obtain a fresh-context review; resolve important findings with regression tests. +- [ ] Push `feat/crewai-automation`, create a PR and verify CI for the exact head. +- [ ] Keep the user's preview available with honest missing-service state; do not merge or deploy. diff --git a/docs/superpowers/specs/2026-10-29-automation-design.md b/docs/superpowers/specs/2026-10-29-automation-design.md new file mode 100644 index 0000000..457a144 --- /dev/null +++ b/docs/superpowers/specs/2026-10-29-automation-design.md @@ -0,0 +1,57 @@ +# Automation workspace + +## Intent and approved scope + +Add the existing CrewAI API to this UI as a separate Automation workspace. +The operator approved the existing-API proposal: flow/agent discovery, explicit +manual execution and inspection of returned results. Backend services remain +separate. Only `justgithubaccount/app-poly-gitops-ui` is a mutation target. + +## Approaches + +1. **Selected:** a separate `/automation` workspace and fixed-upstream proxy. + Reuses the application shell, semantic tokens, read states and API validation. +2. Merge the services or introduce a gateway: unnecessary backend changes. +3. Add an independent frontend: duplicates setup and fragments the workspace. + +## API and interface + +Verified source: `app-poly-gitops-crewai@0835d3102555e6cef4fb700c56969ecddc6f6ddd`, +`src/auto_k8s_pilot/api.py` and `flow_runner.py`. + +- `GET /health`, `/flows`, `/agents`: independent regions with loading, empty, + error, refresh and stale states. An unavailable CrewAI must not break chat. +- `POST /run` and `/run/{flow_name}`: select the server default or a discovered + flow; enter namespace, optional app name and optional JSON object context. +- Before each run require an explicit acknowledgement that flows may call an + LLM, change infrastructure or create issues. Never start on load or retry POST. +- Show pending state while waiting, then final answer, ordered step outputs, + agents/tasks, durations, metadata and trace ID if supplied. Results are only + retained in the mounted workspace. They are not a persisted run history. +- Failures preserve the form. Timeout/disconnect means execution may continue; + another run requires fresh acknowledgement. No cancellation/progress promises. +- Reject context keys `namespace` and `app_name`: the API merges context last, + so accepting them would silently override the visible target fields. +- Mobile navigation must wrap and all content must fit at 320/390/1440px. + +## Boundary and configuration + +Server-only `CREWAI_API_URL` (unset by default) and +`CREWAI_API_TIMEOUT_MS=120000`, allowed range 1000–300000ms. Browser calls only +`/api/automation/...`. Preserve the existing Chat API configuration/defaults. +Share transport/error handling; maintain separate route/body allowlists. +No arbitrary URLs/paths, browser credentials, upstream redirects or automatic +execution. Unsupported routes/methods, invalid names, non-object JSON context, +cross-origin writes and excessive payloads are rejected before upstream calls. + +## Non-goals and validation + +No backend mutation, deployment, paid LLM call or real flow execution is part of +implementation verification. No run persistence, live streaming, cancellation, +editable agents/flows, writable provider settings or authentication is invented. +Source-public is not runtime-public: the current services have no user boundary. + +Verify unit contracts/proxy guards and browser discovery, confirmed execution, +result inspection, errors, no auto-retry, missing configuration, stale lists and +responsive layout. Also exercise the real UI proxy against a loopback HTTP +fixture; keep fixture evidence separate from real CrewAI execution. diff --git a/docs/ui-foundation.md b/docs/ui-foundation.md index 484b445..3651714 100644 --- a/docs/ui-foundation.md +++ b/docs/ui-foundation.md @@ -79,11 +79,29 @@ initially expose only local appearance preferences and API diagnostics. Provider/model selection and credentials remain on FastAPI. This UI never collects or persists the optional `userApiKey` field. +## CrewAI contract + +Verified `app-poly-gitops-crewai@0835d3102555e6cef4fb700c56969ecddc6f6ddd`: +`GET /health`, `/flows`, `/agents`; `POST /run` and `/run/{flow_name}`. +The `/automation` workspace covers these endpoints through `/api/automation`. +Discovery/health use independent SWR keys and local read states. +Runs accept `namespace`, optional `app_name` and optional object `context`. +The UI/proxy reject context keys that override the visible target fields. + +Results contain `final_answer`, `steps[{agent,task,output,duration_ms}]` and +`metadata`, optionally including `total_duration_ms` and `trace_id`. Outputs +are rendered as plain text, not executable HTML. The API returns steps only +after completion; it has no run listing, persistence, progress or cancellation. +Results remain only in the mounted UI, not a fabricated history. +Manual acknowledgement is mandatory for each submission and is invalidated +when form inputs change. Backend mutation policy and credentials remain in +CrewAI; the UI cannot claim that an arbitrary flow is read-only. + The source repository can be public; runtime access is a separate concern. The current backend has no authentication/ownership boundary. Local startup binds only to loopback. A public multi-user deployment requires an explicit -backend access contract. CrewAI is a separate service and needs a verified API -contract before any corresponding UI controls are introduced. +backend access contract. CrewAI remains a separate service with the verified +contract above; adding UI controls does not grant cluster access. ## Code and environment setup @@ -99,11 +117,14 @@ src/components/layout/ responsive application shell src/features/projects/ project list/create src/features/chat/ composer, messages, request-history inspection src/features/settings/ appearance and diagnostics +src/features/automation/ service catalog, manual runs and result inspection src/lib/api/ schemas, client and proxy helpers src/lib/async/ shared read-state resolver ``` `CHAT_API_URL` and `CHAT_API_TIMEOUT_MS` are server-only runtime variables. +CrewAI uses separate server-only `CREWAI_API_URL` (unset by default) and +`CREWAI_API_TIMEOUT_MS` (120000ms, allowed 1000–300000ms). The browser uses `/api/backend/...`; the route has a fixed configured upstream and a method/path allowlist. It never accepts arbitrary upstream URLs, forwards browser credentials or follows redirects. FastAPI uses `detail` for diff --git a/package.json b/package.json index 772e005..1ced2b5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "test": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", + "test:integration:automation": "node tests/integration/automation-proxy.mjs", "check": "npm run typecheck && npm run lint && npm test", "format:check": "prettier --check .", "format": "prettier --write ." diff --git a/playwright.config.ts b/playwright.config.ts index 9272cd5..2691829 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -3,6 +3,7 @@ import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ testDir: "./tests/browser", fullyParallel: true, + workers: 2, forbidOnly: Boolean(process.env.CI), retries: 0, reporter: [["list"], ["html", { open: "never" }]], @@ -13,7 +14,11 @@ export default defineConfig({ reducedMotion: "reduce", channel: process.env.PLAYWRIGHT_CHANNEL || undefined, }, - projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { name: "webkit", use: { ...devices["Desktop Safari"] } }, + ], webServer: { command: "npm run start", url: "http://127.0.0.1:3100", diff --git a/src/app/api/automation/[...path]/route.ts b/src/app/api/automation/[...path]/route.ts new file mode 100644 index 0000000..fa00171 --- /dev/null +++ b/src/app/api/automation/[...path]/route.ts @@ -0,0 +1,14 @@ +import "server-only"; +import { proxyAutomationRequest } from "@/lib/api/automation-proxy"; + +export const runtime = "nodejs"; + +type RouteContext = { params: Promise<{ path: string[] }> }; + +async function handle(request: Request, context: RouteContext) { + const { path } = await context.params; + return proxyAutomationRequest(request, path); +} + +export const GET = handle; +export const POST = handle; diff --git a/src/app/automation/page.tsx b/src/app/automation/page.tsx new file mode 100644 index 0000000..87586ad --- /dev/null +++ b/src/app/automation/page.tsx @@ -0,0 +1,8 @@ +import type { Metadata } from "next"; +import { AutomationWorkspace } from "@/features/automation/automation-workspace"; + +export const metadata: Metadata = { title: "Автоматизация" }; + +export default function AutomationPage() { + return ; +} diff --git a/src/components/icons/index.tsx b/src/components/icons/index.tsx index 8ac856b..ba33b5b 100644 --- a/src/components/icons/index.tsx +++ b/src/components/icons/index.tsx @@ -12,6 +12,7 @@ import { Send, Settings, Sun, + Workflow, type LucideProps, } from "lucide-react"; @@ -29,6 +30,7 @@ const icons = { send: Send, settings: Settings, light: Sun, + automation: Workflow, } as const; export type IconName = keyof typeof icons; diff --git a/src/components/layout/app-shell.tsx b/src/components/layout/app-shell.tsx index b80226b..aec9ffb 100644 --- a/src/components/layout/app-shell.tsx +++ b/src/components/layout/app-shell.tsx @@ -8,6 +8,7 @@ import { cn } from "@/lib/utils"; const navigation: { href: string; label: string; icon: IconName }[] = [ { href: "/", label: "Проекты", icon: "project" }, { href: "/chat", label: "Быстрый чат", icon: "chat" }, + { href: "/automation", label: "Автоматизация", icon: "automation" }, { href: "/settings", label: "Настройки", icon: "settings" }, ]; @@ -29,7 +30,10 @@ export function AppShell({ children }: { children: React.ReactNode }) { > Chat API -