From 9e7a2ff175098ffae828e72793046f0aed553e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 03:26:59 +0700 Subject: [PATCH 01/14] Scaffold Next frontend and Fastify backend --- .gitignore | 8 ++ README.md | 139 +++++++++++++++++------------ apps/api/package.json | 9 ++ apps/api/src/agent-service.ts | 40 +++++++++ apps/api/src/config.ts | 7 ++ apps/api/src/server.ts | 41 +++++++++ apps/api/tsconfig.json | 12 +++ apps/web/app/globals.css | 66 ++++++++++++++ apps/web/app/layout.tsx | 15 ++++ apps/web/app/page.tsx | 27 ++++++ apps/web/components/icons.tsx | 13 +++ apps/web/components/operations.tsx | 74 +++++++++++++++ apps/web/components/workspace.tsx | 97 ++++++++++++++++++++ apps/web/next-env.d.ts | 2 + apps/web/next.config.mjs | 7 ++ apps/web/package.json | 9 ++ package.json | 40 +++++++++ scripts/validate-structure.mjs | 32 +++++++ tsconfig.json | 23 +++++ 19 files changed, 602 insertions(+), 59 deletions(-) create mode 100644 apps/api/package.json create mode 100644 apps/api/src/agent-service.ts create mode 100644 apps/api/src/config.ts create mode 100644 apps/api/src/server.ts create mode 100644 apps/api/tsconfig.json create mode 100644 apps/web/app/globals.css create mode 100644 apps/web/app/layout.tsx create mode 100644 apps/web/app/page.tsx create mode 100644 apps/web/components/icons.tsx create mode 100644 apps/web/components/operations.tsx create mode 100644 apps/web/components/workspace.tsx create mode 100644 apps/web/next-env.d.ts create mode 100644 apps/web/next.config.mjs create mode 100644 apps/web/package.json create mode 100644 package.json create mode 100644 scripts/validate-structure.mjs create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index d5a18de..b5da811 100644 --- a/.gitignore +++ b/.gitignore @@ -427,3 +427,11 @@ FodyWeavers.xsd *.msix *.msm *.msp + +# Static build output +dist/ + +# Next.js build output +.next/ +apps/web/.next/ +apps/api/dist/ diff --git a/README.md b/README.md index 81fa532..3f507e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# CodeXPR +# CodeXPR — AI-Craft Studio + +AI-Craft Studio là nền tảng IDE thế hệ mới theo triết lý **Chat-to-Build**: người dùng mô tả ý tưởng bằng ngôn ngữ tự nhiên, AI Dev Agent đọc repository, tạo branch, chỉnh sửa mã, chạy kiểm thử, commit, push và tạo Pull Request. + +## Kiến trúc mục tiêu + +```text Frontend (Next.js + React) │ ▼ @@ -19,63 +25,78 @@ Backend (Node.js/Fastify) Commit Push branch Create Pull Request +``` + +## Cấu trúc repository + +```text +apps/web/ # Frontend Next.js 15 + React + app/ # App Router pages, layout và global styles + components/ # UI modules: workspace, tools, analytics, architecture +apps/api/ # Backend Node.js/Fastify skeleton + src/server.ts # REST + WebSocket endpoints + src/agent-service.ts # Agent orchestration service mock +packages/shared/ # Types dùng chung frontend/backend +scripts/validate-structure.mjs +``` + +## Chức năng đã dựng trong source + +- Frontend Next.js + React với bố cục IDE gồm Sidebar, Repositories, Chat, Tasks, Settings và Task Log. +- Chat với AI Dev Agent, gợi ý xác nhận trước khi build và trạng thái Thinking / Working. +- Khu task log mô phỏng luồng Clone Repository → Install → Edit code → Run tests → Commit → Push branch → Create Pull Request. +- Tool Hub gồm Terminal, Configuration, Knowledge Base, Prompt Editor, Metrics và Playground. +- Run Management, Deploy Setup, Authorization Center và Realtime Analytics Dashboard. +- Backend Fastify skeleton với REST endpoints cho repositories, chat, build task, draft PR và WebSocket stream log. +- Shared TypeScript contracts cho messages, repositories, tasks, PR drafts và deploy target. + +## Công nghệ + +- Frontend: Next.js 15, React, CSS responsive/glassmorphism. +- Backend: Node.js, Fastify. +- Queue/Cache dự kiến: Redis + BullMQ. +- Database dự kiến: PostgreSQL. +- Sandbox dự kiến: Docker hoặc Firecracker. +- AI dự kiến: OpenAI Responses API. +- Git dự kiến: GitHub App + GitHub API. +- Deploy dự kiến: Vercel + VPS. + +## Chạy dự án + +Cài dependencies khi có quyền truy cập registry npm: + +```bash +npm install +``` + +Chạy frontend: + +```bash +npm run dev:web +``` + +Chạy backend: + +```bash +npm run dev:api +``` + +Kiểm tra cấu trúc source không cần dependency ngoài: + +```bash +npm run lint +``` + +Build production sau khi đã cài dependencies: + +```bash +npm run build +``` -Chức năng chính -🤖 Chat với AI. -📂 Kết nối GitHub. -🌿 Tự tạo branch. -📝 AI đọc toàn bộ repository. -🔧 Viết và sửa code. -🧪 Chạy build, lint, test. -📊 Hiển thị log thời gian thực. -📄 Tạo Pull Request. -🔄 Chạy nhiều task song song. -💬 Lưu lịch sử hội thoại. -Công nghệ -Frontend: Next.js 15, React, Tailwind CSS, shadcn/ui. -Backend: Node.js, Fastify. -Database: PostgreSQL. -Cache/Queue: Redis + BullMQ. -Sandbox: Docker hoặc Firecracker. -AI: OpenAI Responses API. -Git: GitHub App + GitHub API. -Deploy: Vercel + VPS. -Giao diện -Bố cục có thể gồm: -+-----------------------------------------+ -| Sidebar | -| - Repositories | -| - Chats | -| - Tasks | -| - Settings | -+-------------+---------------------------+ -| | | -| Chat | Task Log | -| | | -| AI | Build | -| | Tests | -| | Git | -+-------------+---------------------------+ -Thương hiệu -Không nên dùng các tên như: -ChatGPT -Codex -OpenAI -Có thể xây dựng thương hiệu riêng, ví dụ: -RKix Studio -RKix Agent -RKix Build -RKix Dev -RKix Cloud -Nova Build -ForgeAI -Khả năng -Sản phẩm có thể đạt khoảng 90–95% trải nghiệm của Codex Cloud, nếu triển khai đầy đủ: -GitHub App. -Sandbox an toàn. -Quản lý nhiều agent. -Streaming phản hồi. -Chỉnh sửa mã trực tiếp. -Pull Request tự động. -Đây là một dự án khá lớn, ước tính khoảng 30.000–50.000 dòng mã nếu xây dựng đầy đủ và ở mức production. Với mục tiêu của mày, nên phát triển theo từng giai đoạn: trước tiên hoàn thành MVP (chat + GitHub + chỉnh sửa mã + tạo PR), sau đó mới bổ sung sandbox nâng cao, chạy nhiều agent và các tính năng tự động hóa khác. +## Lộ trình MVP +1. Hoàn thiện chat + GitHub App + repository indexing. +2. Thêm BullMQ worker để chạy clone/install/edit/test trong sandbox. +3. Tích hợp OpenAI Responses API cho agent loop và self-review. +4. Tạo Pull Request tự động sau khi build/lint/test thành công. +5. Bổ sung sandbox nâng cao, multi-agent, deploy automation và authorization center thật. diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..78e8e5c --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,9 @@ +{ + "name": "@codexpr/api", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx src/server.ts", + "build": "tsc -p tsconfig.json" + } +} diff --git a/apps/api/src/agent-service.ts b/apps/api/src/agent-service.ts new file mode 100644 index 0000000..d29b152 --- /dev/null +++ b/apps/api/src/agent-service.ts @@ -0,0 +1,40 @@ +import type { AgentTask, ChatMessage, PullRequestDraft, RepositorySummary } from '@codexpr/shared'; + +export class AgentService { + private readonly repositories: RepositorySummary[] = [ + { id: 'repo-1', name: 'nova-commerce', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/checkout-flow', status: 'ready' }, + ]; + + async listRepositories() { + return this.repositories; + } + + async createChatTurn(content: string): Promise { + return { + id: `agent-${Date.now()}`, + role: 'agent', + content: `Đã nhận yêu cầu: "${content}". Gợi ý: chọn scope, xác nhận branch, chạy sandbox build.`, + createdAt: new Date().toISOString(), + suggestions: ['Xác nhận scope', 'Tạo branch', 'Chạy build', 'Mở PR'], + }; + } + + async queueBuildTask(repositoryId: string): Promise { + return { + id: `task-${Date.now()}`, + title: `Build ${repositoryId}`, + status: 'queued', + progress: 0, + logs: ['Task queued for BullMQ worker', 'Waiting for sandbox capacity'], + }; + } + + async draftPullRequest(): Promise { + return { + repository: 'nova-commerce', + branch: 'agent/checkout-flow', + title: 'AI Agent build: checkout flow', + checks: ['build', 'lint', 'test', 'self-review'], + }; + } +} diff --git a/apps/api/src/config.ts b/apps/api/src/config.ts new file mode 100644 index 0000000..c23f230 --- /dev/null +++ b/apps/api/src/config.ts @@ -0,0 +1,7 @@ +export const config = { + port: Number(process.env.PORT ?? 4000), + databaseUrl: process.env.DATABASE_URL ?? 'postgres://localhost:5432/codexpr', + redisUrl: process.env.REDIS_URL ?? 'redis://localhost:6379', + githubAppId: process.env.GITHUB_APP_ID ?? 'local-dev', + sandboxDriver: process.env.SANDBOX_DRIVER ?? 'docker', +}; diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts new file mode 100644 index 0000000..a9655c4 --- /dev/null +++ b/apps/api/src/server.ts @@ -0,0 +1,41 @@ +import Fastify from 'fastify'; +import cors from '@fastify/cors'; +import websocket from '@fastify/websocket'; +import { z } from 'zod'; +import { AgentService } from './agent-service.js'; +import { config } from './config.js'; + +const server = Fastify({ logger: true }); +const agentService = new AgentService(); + +await server.register(cors, { origin: true }); +await server.register(websocket); + +server.get('/health', async () => ({ status: 'ok', service: 'codexpr-api' })); + +server.get('/repositories', async () => agentService.listRepositories()); + +server.post('/chat', async (request) => { + const body = z.object({ content: z.string().min(1) }).parse(request.body); + return agentService.createChatTurn(body.content); +}); + +server.post('/tasks/build', async (request) => { + const body = z.object({ repositoryId: z.string().min(1) }).parse(request.body); + return agentService.queueBuildTask(body.repositoryId); +}); + +server.post('/pull-requests/draft', async () => agentService.draftPullRequest()); + +server.get('/logs/stream', { websocket: true }, (connection) => { + const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; + let index = 0; + const timer = setInterval(() => { + connection.socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); + index += 1; + if (index >= events.length) clearInterval(timer); + }, 1000); + connection.socket.on('close', () => clearInterval(timer)); +}); + +server.listen({ port: config.port, host: '0.0.0.0' }); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..9bd0d84 --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "../..", + "noEmit": false, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css new file mode 100644 index 0000000..639eb6b --- /dev/null +++ b/apps/web/app/globals.css @@ -0,0 +1,66 @@ +:root { + --bg: #050816; + --panel: rgba(15, 23, 42, 0.72); + --panel-soft: rgba(255, 255, 255, 0.07); + --line: rgba(255, 255, 255, 0.14); + --text: #eef4ff; + --muted: #9fb0c7; + --cyan: #67e8f9; + --violet: #a78bfa; + --green: #86efac; + --amber: #fcd34d; +} + +* { box-sizing: border-box; } +html { scroll-behavior: smooth; } +body { margin: 0; min-width: 320px; background: var(--bg); color: var(--text); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } +button, input, select { font: inherit; } +button { cursor: pointer; } + +.app-shell { + position: relative; + display: grid; + grid-template-columns: 280px minmax(0, 1fr); + gap: 22px; + min-height: 100vh; + padding: 22px; + background: radial-gradient(circle at 16% 12%, rgba(124, 58, 237, .34), transparent 28rem), radial-gradient(circle at 86% 4%, rgba(6, 182, 212, .28), transparent 26rem), radial-gradient(circle at 72% 94%, rgba(34, 197, 94, .14), transparent 28rem), var(--bg); +} + +.background-grid { position: fixed; inset: 0; pointer-events: none; background-image: linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px); background-size: 54px 54px; mask-image: radial-gradient(circle at center, #000 18%, transparent 75%); } +.glass { border: 1px solid var(--line); background: linear-gradient(145deg, rgba(15,23,42,.78), rgba(15,23,42,.42)); box-shadow: 0 24px 80px rgba(0,0,0,.32), inset 0 1px 0 rgba(255,255,255,.08); backdrop-filter: blur(20px); } +.sidebar { position: sticky; top: 22px; z-index: 2; height: calc(100vh - 44px); padding: 20px; border-radius: 30px; display: flex; flex-direction: column; gap: 24px; } +.brand { display: flex; align-items: center; gap: 10px; font-weight: 900; letter-spacing: -.03em; font-size: 1.12rem; } +.glyph { width: 38px; height: 38px; display: inline-grid; place-items: center; border-radius: 14px; border: 1px solid rgba(255,255,255,.12); background: rgba(103,232,249,.1); color: var(--cyan); } +.glyph-violet { color: var(--violet); background: rgba(124,58,237,.14); } .glyph-green { color: var(--green); background: rgba(34,197,94,.1); } .glyph-amber { color: var(--amber); background: rgba(245,158,11,.12); } +nav { display: grid; gap: 8px; } nav a { padding: 12px 14px; border-radius: 16px; color: #b7c5d8; text-decoration: none; transition: .2s ease; } nav a:hover, nav a.active { color: white; background: rgba(255,255,255,.1); } +.trust-card { margin-top: auto; padding: 18px; border-radius: 22px; background: var(--panel-soft); } .trust-card p, .repo-card p, .task-row p, .auth-row p, .auth-row small { color: var(--muted); line-height: 1.55; } +.content-shell { position: relative; z-index: 1; display: grid; gap: 22px; } +.hero { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(320px, .8fr); gap: 26px; align-items: center; padding: clamp(24px, 4vw, 54px); border-radius: 36px; overflow: hidden; } +.hero h1 { margin: 18px 0; max-width: 900px; font-size: clamp(2.45rem, 6vw, 6.8rem); line-height: .9; letter-spacing: -.08em; } +.hero p { max-width: 760px; color: #a8b7cf; font-size: clamp(1rem, 1.7vw, 1.2rem); line-height: 1.7; } +.hero-actions, .control-row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; } +.primary-action, .secondary-action, button { display: inline-flex; align-items: center; justify-content: center; gap: 9px; border: 0; border-radius: 999px; padding: 12px 18px; color: white; background: linear-gradient(135deg, #7c3aed, #06b6d4); box-shadow: 0 14px 40px rgba(6,182,212,.2); text-decoration: none; } +.secondary-action, .control-row button:not(:first-child), .suggestion-row button, .tool-tabs button { background: rgba(255,255,255,.1); box-shadow: none; border: 1px solid rgba(255,255,255,.12); } +.square-panel { aspect-ratio: 1 / 1; min-height: 280px; } +.agent-stage { position: relative; display: grid; place-items: center; border-radius: 34px; background: radial-gradient(circle, rgba(103,232,249,.2), rgba(124,58,237,.08) 45%, rgba(255,255,255,.04)); overflow: hidden; } +.agent-mark { position: relative; width: min(68%, 330px); aspect-ratio: 1; display: grid; place-items: center; border-radius: 50%; background: radial-gradient(circle at 35% 25%, rgba(255,255,255,.38), rgba(103,232,249,.2) 22%, rgba(124,58,237,.28) 58%, rgba(15,23,42,.74)); box-shadow: 0 0 80px rgba(103,232,249,.26); } +.agent-core { font-size: 4rem; } .agent-ring { position: absolute; border: 1px solid rgba(103,232,249,.28); border-radius: 50%; animation: pulse 2.8s ease-in-out infinite; } .agent-ring-a { inset: -28px; } .agent-ring-b { inset: -58px; animation-delay: .8s; } +.agent-state { position: absolute; left: 22px; bottom: 24px; display: flex; align-items: center; gap: 8px; border-radius: 999px; padding: 10px 14px; background: rgba(15,23,42,.72); color: #c4f1ff; } .agent-state.working { left: auto; right: 22px; top: 24px; bottom: auto; color: #dcfce7; } .agent-state span { width: 9px; height: 9px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 18px #22c55e; } +.workspace-layout { display: grid; grid-template-columns: .85fr 1.2fr .95fr; gap: 22px; } .operations-layout { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; } +.panel { border-radius: 30px; padding: 22px; min-width: 0; } .section-heading { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; } .section-heading p { margin: 0; color: #7dd3fc; font-size: .78rem; text-transform: uppercase; letter-spacing: .12em; } .section-heading h2 { margin: 3px 0 0; font-size: 1.15rem; } +.pill { display: inline-flex; width: fit-content; align-items: center; border-radius: 999px; padding: 6px 10px; font-size: .78rem; border: 1px solid currentColor; } .cyan { color: var(--cyan); background: rgba(6,182,212,.1); } .green { color: var(--green); background: rgba(34,197,94,.1); } .amber { color: var(--amber); background: rgba(245,158,11,.12); } .violet { color: var(--violet); background: rgba(124,58,237,.14); } +.repo-card, .task-row, .auth-row { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center; padding: 14px; border-radius: 18px; background: var(--panel-soft); margin-top: 10px; } .repo-card h3, .auth-row h3 { margin: 0; } .repo-card p, .task-row p, .auth-row p { margin: 4px 0 0; } +.suggestion-row, .tool-tabs { display: flex; flex-wrap: wrap; gap: 9px; } .tool-tabs button span { margin-left: 8px; color: var(--cyan); font-size: .76rem; } +.message-list { display: grid; gap: 10px; margin: 18px 0; } .message { padding: 14px; border-radius: 18px; line-height: 1.5; color: #d7e3f7; background: var(--panel-soft); } .message.user { margin-left: 12%; background: rgba(124,58,237,.18); } .message.agent { margin-right: 10%; border: 1px solid rgba(103,232,249,.16); } .message p { margin: 0; } .inline-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } .inline-suggestions span { border-radius: 999px; padding: 6px 10px; background: rgba(103,232,249,.12); color: #cffafe; } +.composer { display: grid; grid-template-columns: 1fr auto; gap: 9px; } .composer input, .deploy-form input, .deploy-form select { width: 100%; border: 1px solid rgba(255,255,255,.12); border-radius: 16px; padding: 13px 14px; background: rgba(3,7,18,.42); color: #e5eefc; } +.progress { grid-column: 1 / -1; height: 8px; border-radius: 999px; background: rgba(255,255,255,.1); overflow: hidden; } .progress span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #7c3aed, #06b6d4, #22c55e); } +.terminal-log { margin: 16px 0 0; border-radius: 18px; padding: 14px; background: rgba(2,6,23,.72); overflow: auto; } .ok { color: #86efac; } .info { color: #67e8f9; } .warn { color: #fcd34d; } +.command-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } .command-grid article { display: grid; gap: 8px; padding: 14px; border-radius: 18px; background: var(--panel-soft); } code { color: var(--cyan); } +.deploy-form { display: grid; grid-template-columns: 1fr 1fr auto; gap: 10px; } .deploy-progress { margin-top: 16px; } .activity-log { color: #b9c7db; line-height: 1.7; } +.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } .stats article { padding: 16px; border-radius: 18px; background: var(--panel-soft); } .stats span { color: var(--muted); } .stats strong { display: block; margin-top: 8px; font-size: 1.8rem; } +.chart-grid { display: grid; grid-template-columns: 1fr 1fr .75fr; gap: 16px; margin-top: 20px; } .chart { min-height: 230px; border-radius: 22px; background: linear-gradient(180deg, rgba(124,58,237,.14), rgba(6,182,212,.06)); position: relative; overflow: hidden; padding: 18px; } .line-chart span { position: absolute; inset: 58px 20px; background: linear-gradient(135deg, transparent 45%, rgba(167,139,250,.8) 46%, rgba(167,139,250,.8) 48%, transparent 49%); } .bar-chart { display: flex; align-items: end; gap: 12px; } .bar-chart strong { position: absolute; top: 18px; left: 18px; } .bar-chart i { flex: 1; border-radius: 10px 10px 0 0; background: linear-gradient(#06b6d4, #7c3aed); } .pie-chart span { display: block; width: 150px; height: 150px; border-radius: 50%; margin: 32px auto 0; background: conic-gradient(#7c3aed 0 38%, #06b6d4 38% 62%, #22c55e 62% 83%, #f59e0b 83%); } +.architecture-flow { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; } .architecture-flow div { padding: 14px; border-radius: 18px; background: var(--panel-soft); color: #dbeafe; } +@keyframes pulse { 0%, 100% { opacity: .2; transform: scale(.92); } 50% { opacity: .72; transform: scale(1.02); } } +@media (max-width: 1180px) { .app-shell { grid-template-columns: 1fr; } .sidebar { position: relative; top: auto; height: auto; } nav { grid-template-columns: repeat(3, 1fr); } .workspace-layout, .operations-layout, .chart-grid, .architecture-flow { grid-template-columns: 1fr; } } +@media (max-width: 760px) { .app-shell { padding: 12px; } .hero { grid-template-columns: 1fr; border-radius: 24px; } nav { grid-template-columns: 1fr 1fr; } .deploy-form, .stats, .command-grid { grid-template-columns: 1fr; } .panel { padding: 16px; border-radius: 22px; } } diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx new file mode 100644 index 0000000..37238eb --- /dev/null +++ b/apps/web/app/layout.tsx @@ -0,0 +1,15 @@ +import type { Metadata } from 'next'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'AI-Craft Studio', + description: 'Chat-to-Build IDE powered by AI Dev Agents.', +}; + +export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { + return ( + + {children} + + ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx new file mode 100644 index 0000000..7a35ed9 --- /dev/null +++ b/apps/web/app/page.tsx @@ -0,0 +1,27 @@ +import { AnalyticsDashboard, Architecture, AuthorizationCenter, DeploymentDashboard, RunManagement, ToolHub } from '../components/operations'; +import { ChatPanel, Hero, RepositoryPanel, Sidebar, TaskLogPanel } from '../components/workspace'; + +export default function HomePage() { + return ( +
+
+ +
+ +
+ + + +
+
+ + + + +
+ + +
+
+ ); +} diff --git a/apps/web/components/icons.tsx b/apps/web/components/icons.tsx new file mode 100644 index 0000000..2db9dcb --- /dev/null +++ b/apps/web/components/icons.tsx @@ -0,0 +1,13 @@ +export function Glyph({ children, tone = 'cyan' }: { children: React.ReactNode; tone?: 'cyan' | 'violet' | 'green' | 'amber' }) { + return {children}; +} + +export function AgentMark() { + return ( +
+ + + +
+ ); +} diff --git a/apps/web/components/operations.tsx b/apps/web/components/operations.tsx new file mode 100644 index 0000000..763eed4 --- /dev/null +++ b/apps/web/components/operations.tsx @@ -0,0 +1,74 @@ +import { Glyph } from './icons'; + +const runCommands = [ + ['Install', 'npm install', 'success'], + ['Build', 'npm run build', 'running'], + ['Lint', 'npm run lint', 'queued'], + ['Test', 'npm test', 'queued'], +]; + +const authRequests = [ + { app: 'Google Drive', url: 'https://drive.google.com', scope: 'Read docs', state: 'Chờ duyệt' }, + { app: 'GitHub', url: 'https://github.com/rkix/nova-commerce', scope: 'Create branch + PR', state: 'Đã cấp quyền' }, + { app: 'Vercel', url: 'https://vercel.com', scope: 'Deploy preview', state: 'Đã cấp quyền' }, +]; + +export function ToolHub() { + const tabs = ['Terminal / Console', 'Configuration', 'Knowledge Base', 'Prompt Editor', 'Monitoring & Metrics', 'Testing / Playground']; + return ( +
+

Tool Hub

Tiện ích AI Agent Dev

+
{tabs.map((tab, index) => )}
+
+ ); +} + +export function RunManagement() { + return ( +
+

Run Management

Tự động hóa Terminal

+
{runCommands.map(([name, command, state]) =>
{name}{command}{state}
)}
+
+
+ ); +} + +export function DeploymentDashboard() { + return ( +
+

Deploy Setup

Cấu hình triển khai tự động

+
+
+
  • Đang kiểm tra kết nối
  • Đang cài đặt phụ thuộc
  • Đang cấu hình server
  • Preview URL đã sẵn sàng
+
+ ); +} + +export function AuthorizationCenter() { + return ( +
+
🔐

Authorization

Phê duyệt quyền AI Agent

+ {authRequests.map((request) =>

{request.app}

{request.url}

{request.scope}
{request.state}
)} +
+ ); +} + +export function AnalyticsDashboard() { + return ( +
+
📊

Realtime Analytics

Hiệu suất Agent

+
Tasks128
PRs24
Success94%
Cost$8.42
+
Token trend
Task throughput
Workflow mix
+
+ ); +} + +export function Architecture() { + const items = ['Frontend Next.js + React', 'Backend Node.js/Fastify', 'OpenAI Responses API', 'GitHub App', 'BullMQ Task Queue', 'PostgreSQL', 'Redis', 'Sandbox Docker/Firecracker', 'Clone → Install → Edit → Test → Commit → Push → PR']; + return ( +
+

Architecture

Luồng hệ thống sản phẩm

+
{items.map((item) =>
{item}
)}
+
+ ); +} diff --git a/apps/web/components/workspace.tsx b/apps/web/components/workspace.tsx new file mode 100644 index 0000000..823395f --- /dev/null +++ b/apps/web/components/workspace.tsx @@ -0,0 +1,97 @@ +import type { AgentTask, ChatMessage, RepositorySummary } from '@codexpr/shared'; +import { AgentMark, Glyph } from './icons'; + +const repositories: RepositorySummary[] = [ + { id: 'repo-1', name: 'nova-commerce', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/checkout-flow', status: 'ready' }, + { id: 'repo-2', name: 'booking-agent', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/calendar-ui', status: 'indexing' }, + { id: 'repo-3', name: 'data-portal', provider: 'github', defaultBranch: 'main', status: 'connected' }, +]; + +const messages: ChatMessage[] = [ + { id: 'm1', role: 'user', content: 'Tạo một trang bán giày có thanh toán và dashboard.', createdAt: '09:20' }, + { id: 'm2', role: 'agent', content: 'Gợi ý tối ưu: storefront, checkout, inventory, analytics. Vui lòng xác nhận để bắt đầu build MVP.', createdAt: '09:21', suggestions: ['Storefront', 'Checkout', 'Inventory', 'Analytics'] }, + { id: 'm3', role: 'user', content: 'Xác nhận. Ưu tiên giao diện cao cấp và live preview.', createdAt: '09:22' }, +]; + +const tasks: AgentTask[] = [ + { id: 't1', title: 'Clone repository', status: 'succeeded', progress: 100, logs: ['git clone complete'] }, + { id: 't2', title: 'Install dependencies', status: 'succeeded', progress: 100, logs: ['npm install complete'] }, + { id: 't3', title: 'Edit code', status: 'running', progress: 68, logs: ['writing checkout components'] }, + { id: 't4', title: 'Run tests', status: 'queued', progress: 0, logs: ['waiting for edit step'] }, + { id: 't5', title: 'Create Pull Request', status: 'queued', progress: 0, logs: ['waiting for checks'] }, +]; + +export function Sidebar() { + return ( + + ); +} + +export function Hero() { + return ( +
+
+ Next.js + React · Fastify Backend +

Chat với AI. Build repo. Tạo Pull Request.

+

AI-Craft Studio gom chat, GitHub, sandbox, task queue, logs, kiểm thử và deploy thành một IDE thế hệ mới cho người dùng không cần thao tác code thủ công.

+ +
+
+ +
Thinking...
+
Working...
+
+
+ ); +} + +export function RepositoryPanel() { + return ( +
+
📂

GitHub

Repositories

+ {repositories.map((repo) => ( +
+

{repo.name}

{repo.activeBranch ?? repo.defaultBranch}

+ {repo.status} +
+ ))} +
+ ); +} + +export function ChatPanel() { + return ( +
+
🤖

Central Conversation

AI Dev Agent

+
{['MVP', 'Checkout', 'Build tests', 'Open PR'].map((item) => )}
+
+ {messages.map((message) =>

{message.content}

{message.suggestions &&
{message.suggestions.map((item) => {item})}
}
)} +
+ +
+ ); +} + +export function TaskLogPanel() { + return ( +
+
🧪

Realtime Logs

Build · Tests · Git

+
+ {tasks.map((task) =>
{task.title}

{task.logs.at(-1)}

{task.status}
)} +
+
✓ cloned repository{'\n'}✓ dependencies installed{'\n'}› editing app/checkout/page.tsx{'\n'}… tests queued after self-review
+
+ ); +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 0000000..6080add --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs new file mode 100644 index 0000000..57c2e78 --- /dev/null +++ b/apps/web/next.config.mjs @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + transpilePackages: ['@codexpr/shared'], +}; + +export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..f9e18f0 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,9 @@ +{ + "name": "@codexpr/web", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev --hostname 0.0.0.0 --port 3000", + "build": "next build" + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1aca195 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "codexpr-ai-craft-studio", + "private": true, + "version": "0.2.0", + "type": "module", + "workspaces": [ + "apps/web", + "apps/api", + "packages/shared" + ], + "scripts": { + "dev": "npm run dev:web", + "dev:web": "next dev apps/web --hostname 0.0.0.0 --port 3000", + "dev:api": "tsx apps/api/src/server.ts", + "build": "npm run build:web && npm run build:api", + "build:web": "next build apps/web", + "build:api": "tsc -p apps/api/tsconfig.json", + "lint": "node scripts/validate-structure.mjs", + "typecheck": "tsc --noEmit -p tsconfig.json" + }, + "dependencies": { + "@fastify/cors": "latest", + "@fastify/websocket": "latest", + "bullmq": "latest", + "fastify": "latest", + "ioredis": "latest", + "next": "15.0.0", + "pg": "latest", + "react": "latest", + "react-dom": "latest", + "zod": "latest" + }, + "devDependencies": { + "@types/node": "latest", + "@types/react": "latest", + "@types/react-dom": "latest", + "tsx": "latest", + "typescript": "latest" + } +} diff --git a/scripts/validate-structure.mjs b/scripts/validate-structure.mjs new file mode 100644 index 0000000..149ad00 --- /dev/null +++ b/scripts/validate-structure.mjs @@ -0,0 +1,32 @@ +import { readFile, stat } from 'node:fs/promises'; + +const requiredFiles = [ + 'apps/web/app/page.tsx', + 'apps/web/app/layout.tsx', + 'apps/web/app/globals.css', + 'apps/web/components/workspace.tsx', + 'apps/web/components/operations.tsx', + 'apps/api/src/server.ts', + 'apps/api/src/agent-service.ts', + 'packages/shared/src/index.ts', +]; + +const requiredTokens = new Map([ + ['apps/web/app/page.tsx', ['RepositoryPanel', 'ChatPanel', 'TaskLogPanel', 'Architecture']], + ['apps/web/components/workspace.tsx', ['Clone repository', 'Install dependencies', 'Create Pull Request', 'Thinking']], + ['apps/web/components/operations.tsx', ['Run Management', 'Deploy Setup', 'Authorization', 'Realtime Analytics']], + ['apps/api/src/server.ts', ['/chat', '/tasks/build', '/pull-requests/draft', '/logs/stream']], +]); + +for (const file of requiredFiles) await stat(file); + +for (const [file, tokens] of requiredTokens) { + const content = await readFile(file, 'utf8'); + const missing = tokens.filter((token) => !content.includes(token)); + if (missing.length) { + console.error(`${file} is missing: ${missing.join(', ')}`); + process.exit(1); + } +} + +console.log('Project structure checks passed'); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f509d91 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "baseUrl": ".", + "paths": { + "@codexpr/shared": ["packages/shared/src/index.ts"], + "@/*": ["apps/web/*"] + } + }, + "include": ["apps/**/*.ts", "apps/**/*.tsx", "packages/**/*.ts"], + "exclude": ["node_modules", "dist", ".next"] +} From 5882661d53a7d7a14fc89b3e56bb28e8cbc0f626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 03:44:50 +0700 Subject: [PATCH 02/14] Expand Next pages and Vercel deployment setup --- README.md | 38 +++++++++++++++++++-- apps/api/src/analytics-service.ts | 11 +++++++ apps/api/src/auth-service.ts | 14 ++++++++ apps/api/src/deployment-service.ts | 21 ++++++++++++ apps/api/src/server.ts | 22 +++++++++++++ apps/web/app/analytics/page.tsx | 6 ++++ apps/web/app/auth-center/page.tsx | 6 ++++ apps/web/app/dashboard/page.tsx | 7 ++++ apps/web/app/deploy/page.tsx | 6 ++++ apps/web/app/globals.css | 11 +++++++ apps/web/app/page.tsx | 40 +++++++++++------------ apps/web/app/run-management/page.tsx | 7 ++++ apps/web/app/settings/page.tsx | 7 ++++ apps/web/app/tool-hub/page.tsx | 6 ++++ apps/web/app/workplace/page.tsx | 6 ++++ apps/web/components/app-shell.tsx | 47 ++++++++++++++++++++++++++ apps/web/components/data.ts | 49 ++++++++++++++++++++++++++++ apps/web/components/operations.tsx | 15 +++------ apps/web/components/workspace.tsx | 22 +------------ package.json | 3 +- scripts/validate-structure.mjs | 18 ++++++++-- vercel.json | 7 ++++ 22 files changed, 311 insertions(+), 58 deletions(-) create mode 100644 apps/api/src/analytics-service.ts create mode 100644 apps/api/src/auth-service.ts create mode 100644 apps/api/src/deployment-service.ts create mode 100644 apps/web/app/analytics/page.tsx create mode 100644 apps/web/app/auth-center/page.tsx create mode 100644 apps/web/app/dashboard/page.tsx create mode 100644 apps/web/app/deploy/page.tsx create mode 100644 apps/web/app/run-management/page.tsx create mode 100644 apps/web/app/settings/page.tsx create mode 100644 apps/web/app/tool-hub/page.tsx create mode 100644 apps/web/app/workplace/page.tsx create mode 100644 apps/web/components/app-shell.tsx create mode 100644 apps/web/components/data.ts create mode 100644 vercel.json diff --git a/README.md b/README.md index 3f507e3..7d5bb67 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,27 @@ Backend (Node.js/Fastify) ```text apps/web/ # Frontend Next.js 15 + React app/ # App Router pages, layout và global styles - components/ # UI modules: workspace, tools, analytics, architecture + components/ # UI modules: app shell, workspace, operations, data, icons apps/api/ # Backend Node.js/Fastify skeleton src/server.ts # REST + WebSocket endpoints - src/agent-service.ts # Agent orchestration service mock + src/*-service.ts # Agent, deploy, auth, analytics mock services packages/shared/ # Types dùng chung frontend/backend scripts/validate-structure.mjs +vercel.json # Cấu hình deploy frontend lên Vercel ``` +## Pages đã bổ sung + +- `/` — landing tổng quan, workplace, operations, analytics và architecture. +- `/dashboard` — bảng điều khiển dự án, repository, task log và analytics. +- `/workplace` — không gian chat-to-build với AI Dev Agent, repository và logs. +- `/tool-hub` — tab phụ trợ: Terminal, Configuration, Knowledge Base, Prompt Editor, Metrics, Playground. +- `/run-management` — quản lý lệnh terminal, workflow và output realtime. +- `/deploy` — thiết lập deploy Production/Staging, env vars, progress và activity logs. +- `/auth-center` — phê duyệt quyền truy cập URL/Google/GitHub/Vercel cho AI Agent. +- `/analytics` — dashboard phân tích realtime với cards và biểu đồ CSS. +- `/settings` — cấu hình hệ thống theo hướng không tham số. + ## Chức năng đã dựng trong source - Frontend Next.js + React với bố cục IDE gồm Sidebar, Repositories, Chat, Tasks, Settings và Task Log. @@ -47,7 +60,7 @@ scripts/validate-structure.mjs - Khu task log mô phỏng luồng Clone Repository → Install → Edit code → Run tests → Commit → Push branch → Create Pull Request. - Tool Hub gồm Terminal, Configuration, Knowledge Base, Prompt Editor, Metrics và Playground. - Run Management, Deploy Setup, Authorization Center và Realtime Analytics Dashboard. -- Backend Fastify skeleton với REST endpoints cho repositories, chat, build task, draft PR và WebSocket stream log. +- Backend Fastify skeleton với REST endpoints cho repositories, chat, build task, deploy setup, authorization, analytics, draft PR và WebSocket stream log. - Shared TypeScript contracts cho messages, repositories, tasks, PR drafts và deploy target. ## Công nghệ @@ -93,6 +106,25 @@ Build production sau khi đã cài dependencies: npm run build ``` +## Deploy lên Vercel + +Repository đã có `vercel.json` trỏ Vercel build vào frontend Next.js: + +```bash +npm run deploy:vercel +``` + +Trong môi trường CI/CD, cấu hình các biến sau trên Vercel trước khi deploy thật: + +- `OPENAI_API_KEY` +- `GITHUB_APP_ID` +- `GITHUB_APP_PRIVATE_KEY` +- `DATABASE_URL` +- `REDIS_URL` +- `SANDBOX_DRIVER` + +Lưu ý: backend Fastify hiện là skeleton chạy riêng; khi deploy production cần chọn chiến lược server riêng/VPS hoặc chuyển các endpoint cần thiết thành Next.js Route Handlers/serverless functions. + ## Lộ trình MVP 1. Hoàn thiện chat + GitHub App + repository indexing. diff --git a/apps/api/src/analytics-service.ts b/apps/api/src/analytics-service.ts new file mode 100644 index 0000000..3b8d3ae --- /dev/null +++ b/apps/api/src/analytics-service.ts @@ -0,0 +1,11 @@ +export class AnalyticsService { + summary() { + return { + tasks: 128, + pullRequests: 24, + successRate: 0.94, + estimatedCostUsd: 8.42, + updatedAt: new Date().toISOString(), + }; + } +} diff --git a/apps/api/src/auth-service.ts b/apps/api/src/auth-service.ts new file mode 100644 index 0000000..678e480 --- /dev/null +++ b/apps/api/src/auth-service.ts @@ -0,0 +1,14 @@ +export class AuthorizationService { + private readonly requests = [ + { id: 'auth-1', app: 'Google Drive', url: 'https://drive.google.com', scope: 'Read docs', state: 'pending' }, + { id: 'auth-2', app: 'GitHub', url: 'https://github.com/rkix/nova-commerce', scope: 'Create branch + PR', state: 'approved' }, + ]; + + listRequests() { + return this.requests; + } + + approve(id: string) { + return { id, state: 'approved', approvedAt: new Date().toISOString() }; + } +} diff --git a/apps/api/src/deployment-service.ts b/apps/api/src/deployment-service.ts new file mode 100644 index 0000000..65a2dfa --- /dev/null +++ b/apps/api/src/deployment-service.ts @@ -0,0 +1,21 @@ +import type { DeployTarget } from '@codexpr/shared'; + +export class DeploymentService { + private readonly targets: DeployTarget[] = [ + { environment: 'production', status: 'processing', url: 'https://nova-commerce.vercel.app' }, + { environment: 'staging', status: 'complete', url: 'https://nova-commerce-staging.vercel.app' }, + ]; + + listTargets() { + return this.targets; + } + + startSetup(environment: DeployTarget['environment']) { + return { + environment, + status: 'processing' as const, + progress: 12, + logs: ['Validate Vercel project', 'Sync encrypted environment variables', 'Prepare build command'], + }; + } +} diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index a9655c4..0a0559f 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -3,10 +3,16 @@ import cors from '@fastify/cors'; import websocket from '@fastify/websocket'; import { z } from 'zod'; import { AgentService } from './agent-service.js'; +import { AnalyticsService } from './analytics-service.js'; +import { AuthorizationService } from './auth-service.js'; import { config } from './config.js'; +import { DeploymentService } from './deployment-service.js'; const server = Fastify({ logger: true }); const agentService = new AgentService(); +const deploymentService = new DeploymentService(); +const authorizationService = new AuthorizationService(); +const analyticsService = new AnalyticsService(); await server.register(cors, { origin: true }); await server.register(websocket); @@ -27,6 +33,22 @@ server.post('/tasks/build', async (request) => { server.post('/pull-requests/draft', async () => agentService.draftPullRequest()); +server.get('/deploy/targets', async () => deploymentService.listTargets()); + +server.post('/deploy/setup', async (request) => { + const body = z.object({ environment: z.enum(['production', 'staging']) }).parse(request.body); + return deploymentService.startSetup(body.environment); +}); + +server.get('/auth/requests', async () => authorizationService.listRequests()); + +server.post('/auth/requests/:id/approve', async (request) => { + const params = z.object({ id: z.string().min(1) }).parse(request.params); + return authorizationService.approve(params.id); +}); + +server.get('/analytics/summary', async () => analyticsService.summary()); + server.get('/logs/stream', { websocket: true }, (connection) => { const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; let index = 0; diff --git a/apps/web/app/analytics/page.tsx b/apps/web/app/analytics/page.tsx new file mode 100644 index 0000000..9aff9dc --- /dev/null +++ b/apps/web/app/analytics/page.tsx @@ -0,0 +1,6 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { AnalyticsDashboard } from '../../components/operations'; + +export default function AnalyticsPage() { + return ; +} diff --git a/apps/web/app/auth-center/page.tsx b/apps/web/app/auth-center/page.tsx new file mode 100644 index 0000000..98f380d --- /dev/null +++ b/apps/web/app/auth-center/page.tsx @@ -0,0 +1,6 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { AuthorizationCenter } from '../../components/operations'; + +export default function AuthCenterPage() { + return ; +} diff --git a/apps/web/app/dashboard/page.tsx b/apps/web/app/dashboard/page.tsx new file mode 100644 index 0000000..eeb30c4 --- /dev/null +++ b/apps/web/app/dashboard/page.tsx @@ -0,0 +1,7 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { AnalyticsDashboard } from '../../components/operations'; +import { RepositoryPanel, TaskLogPanel } from '../../components/workspace'; + +export default function DashboardPage() { + return
; +} diff --git a/apps/web/app/deploy/page.tsx b/apps/web/app/deploy/page.tsx new file mode 100644 index 0000000..9f97b4d --- /dev/null +++ b/apps/web/app/deploy/page.tsx @@ -0,0 +1,6 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { DeploymentDashboard } from '../../components/operations'; + +export default function DeployPage() { + return ; +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 639eb6b..7c95949 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -64,3 +64,14 @@ nav { display: grid; gap: 8px; } nav a { padding: 12px 14px; border-radius: 16px @keyframes pulse { 0%, 100% { opacity: .2; transform: scale(.92); } 50% { opacity: .72; transform: scale(1.02); } } @media (max-width: 1180px) { .app-shell { grid-template-columns: 1fr; } .sidebar { position: relative; top: auto; height: auto; } nav { grid-template-columns: repeat(3, 1fr); } .workspace-layout, .operations-layout, .chart-grid, .architecture-flow { grid-template-columns: 1fr; } } @media (max-width: 760px) { .app-shell { padding: 12px; } .hero { grid-template-columns: 1fr; border-radius: 24px; } nav { grid-template-columns: 1fr 1fr; } .deploy-form, .stats, .command-grid { grid-template-columns: 1fr; } .panel { padding: 16px; border-radius: 22px; } } +.brand { color: var(--text); text-decoration: none; } +.page-hero { border-radius: 30px; padding: clamp(24px, 4vw, 44px); } +.page-hero h1 { max-width: 980px; margin: 16px 0; font-size: clamp(2rem, 5vw, 5rem); line-height: .95; letter-spacing: -.06em; } +.page-hero p { max-width: 860px; margin: 0; color: #a8b7cf; line-height: 1.7; font-size: 1.08rem; } +.utility-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); } +.utility-grid button { align-items: flex-start; text-align: left; flex-direction: column; border-radius: 20px; } +.utility-grid small { color: var(--muted); line-height: 1.5; } +.deploy-targets, .settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; } +.deploy-targets article, .settings-grid article { display: grid; gap: 8px; padding: 14px; border-radius: 18px; background: var(--panel-soft); } +.deploy-targets small { color: var(--muted); } +@media (max-width: 760px) { .utility-grid, .deploy-targets, .settings-grid { grid-template-columns: 1fr; } } diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 7a35ed9..6a3cae1 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,27 +1,25 @@ +import { AppShell, PageHero } from '../components/app-shell'; import { AnalyticsDashboard, Architecture, AuthorizationCenter, DeploymentDashboard, RunManagement, ToolHub } from '../components/operations'; -import { ChatPanel, Hero, RepositoryPanel, Sidebar, TaskLogPanel } from '../components/workspace'; +import { ChatPanel, Hero, RepositoryPanel, TaskLogPanel } from '../components/workspace'; export default function HomePage() { return ( -
-
- -
- -
- - - -
-
- - - - -
- - -
-
+ + +
+ + + +
+
+ + + + +
+ + + +
); } diff --git a/apps/web/app/run-management/page.tsx b/apps/web/app/run-management/page.tsx new file mode 100644 index 0000000..f76a46a --- /dev/null +++ b/apps/web/app/run-management/page.tsx @@ -0,0 +1,7 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { RunManagement } from '../../components/operations'; +import { TaskLogPanel } from '../../components/workspace'; + +export default function RunManagementPage() { + return
; +} diff --git a/apps/web/app/settings/page.tsx b/apps/web/app/settings/page.tsx new file mode 100644 index 0000000..336aef3 --- /dev/null +++ b/apps/web/app/settings/page.tsx @@ -0,0 +1,7 @@ +import { AppShell, PageHero, SectionHeading } from '../../components/app-shell'; + +const settings = ['OpenAI Responses API key', 'GitHub App credentials', 'PostgreSQL connection', 'Redis/BullMQ URL', 'Sandbox driver', 'Vercel project']; + +export default function SettingsPage() { + return
{settings.map((item) =>
{item}Chưa cấu hình
)}
; +} diff --git a/apps/web/app/tool-hub/page.tsx b/apps/web/app/tool-hub/page.tsx new file mode 100644 index 0000000..828859a --- /dev/null +++ b/apps/web/app/tool-hub/page.tsx @@ -0,0 +1,6 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { ToolHub } from '../../components/operations'; + +export default function ToolHubPage() { + return ; +} diff --git a/apps/web/app/workplace/page.tsx b/apps/web/app/workplace/page.tsx new file mode 100644 index 0000000..b803c21 --- /dev/null +++ b/apps/web/app/workplace/page.tsx @@ -0,0 +1,6 @@ +import { AppShell, PageHero } from '../../components/app-shell'; +import { ChatPanel, RepositoryPanel, TaskLogPanel } from '../../components/workspace'; + +export default function WorkplacePage() { + return
; +} diff --git a/apps/web/components/app-shell.tsx b/apps/web/components/app-shell.tsx new file mode 100644 index 0000000..c72a2a1 --- /dev/null +++ b/apps/web/components/app-shell.tsx @@ -0,0 +1,47 @@ +import Link from 'next/link'; +import { Glyph } from './icons'; + +const navItems = [ + ['Dashboard', '/dashboard'], + ['Workplace', '/workplace'], + ['Tool Hub', '/tool-hub'], + ['Run Management', '/run-management'], + ['Deploy', '/deploy'], + ['Auth Center', '/auth-center'], + ['Analytics', '/analytics'], + ['Settings', '/settings'], +]; + +export function AppShell({ children }: { children: React.ReactNode }) { + return ( +
+
+ +
{children}
+
+ ); +} + +export function PageHero({ eyebrow, title, description }: { eyebrow: string; title: string; description: string }) { + return ( +
+ {eyebrow} +

{title}

+

{description}

+
+ ); +} + +export function SectionHeading({ icon, eyebrow, title }: { icon: React.ReactNode; eyebrow: string; title: string }) { + return
{icon}

{eyebrow}

{title}

; +} diff --git a/apps/web/components/data.ts b/apps/web/components/data.ts new file mode 100644 index 0000000..afdfbcd --- /dev/null +++ b/apps/web/components/data.ts @@ -0,0 +1,49 @@ +import type { AgentTask, ChatMessage, DeployTarget, RepositorySummary } from '@codexpr/shared'; + +export const repositories: RepositorySummary[] = [ + { id: 'repo-1', name: 'nova-commerce', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/checkout-flow', status: 'ready' }, + { id: 'repo-2', name: 'booking-agent', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/calendar-ui', status: 'indexing' }, + { id: 'repo-3', name: 'data-portal', provider: 'github', defaultBranch: 'main', status: 'connected' }, + { id: 'repo-4', name: 'studio-docs', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/knowledge-base', status: 'ready' }, +]; + +export const messages: ChatMessage[] = [ + { id: 'm1', role: 'user', content: 'Tạo một trang bán giày có thanh toán và dashboard.', createdAt: '09:20' }, + { id: 'm2', role: 'agent', content: 'Gợi ý tối ưu: storefront, checkout, inventory, analytics. Vui lòng xác nhận để bắt đầu build MVP.', createdAt: '09:21', suggestions: ['Storefront', 'Checkout', 'Inventory', 'Analytics'] }, + { id: 'm3', role: 'user', content: 'Xác nhận. Ưu tiên giao diện cao cấp và live preview.', createdAt: '09:22' }, +]; + +export const tasks: AgentTask[] = [ + { id: 't1', title: 'Clone repository', status: 'succeeded', progress: 100, logs: ['git clone complete'] }, + { id: 't2', title: 'Install dependencies', status: 'succeeded', progress: 100, logs: ['npm install complete'] }, + { id: 't3', title: 'Edit code', status: 'running', progress: 68, logs: ['writing checkout components'] }, + { id: 't4', title: 'Run tests', status: 'queued', progress: 0, logs: ['waiting for edit step'] }, + { id: 't5', title: 'Create Pull Request', status: 'queued', progress: 0, logs: ['waiting for checks'] }, +]; + +export const deployTargets: DeployTarget[] = [ + { environment: 'production', status: 'processing', url: 'https://nova-commerce.vercel.app' }, + { environment: 'staging', status: 'complete', url: 'https://nova-commerce-staging.vercel.app' }, +]; + +export const utilityTabs = [ + { title: 'Terminal / Console', description: 'Realtime logs, build output, lỗi và trạng thái worker.', badge: 'live' }, + { title: 'Configuration', description: 'Quản lý env vars, API keys, model LLM và runtime.', badge: '3 keys' }, + { title: 'Knowledge Base', description: 'Tài liệu, file dữ liệu và nguồn tri thức cho agent.', badge: '12 docs' }, + { title: 'Prompt Editor', description: 'Soạn system prompt, ghi chú và preview prompt.', badge: 'draft' }, + { title: 'Monitoring & Metrics', description: 'Token, latency, chi phí, tỉ lệ thành công.', badge: '$8.42' }, + { title: 'Testing / Playground', description: 'Thử nhanh hội thoại và tool call trước khi deploy.', badge: 'ready' }, +]; + +export const authRequests = [ + { app: 'Google Drive', url: 'https://drive.google.com', scope: 'Read docs', agent: 'Builder Agent', state: 'Chờ duyệt' }, + { app: 'GitHub', url: 'https://github.com/rkix/nova-commerce', scope: 'Create branch + PR', agent: 'Git Agent', state: 'Đã cấp quyền' }, + { app: 'Vercel', url: 'https://vercel.com', scope: 'Deploy preview', agent: 'Deploy Agent', state: 'Đã cấp quyền' }, +]; + +export const analyticsCards = [ + ['Tasks', '128', '+18%'], + ['Pull Requests', '24', '+6'], + ['Success Rate', '94%', '+4%'], + ['Token Cost', '$8.42', '-11%'], +]; diff --git a/apps/web/components/operations.tsx b/apps/web/components/operations.tsx index 763eed4..32d9c35 100644 --- a/apps/web/components/operations.tsx +++ b/apps/web/components/operations.tsx @@ -1,3 +1,4 @@ +import { analyticsCards, authRequests, deployTargets, utilityTabs } from './data'; import { Glyph } from './icons'; const runCommands = [ @@ -7,18 +8,11 @@ const runCommands = [ ['Test', 'npm test', 'queued'], ]; -const authRequests = [ - { app: 'Google Drive', url: 'https://drive.google.com', scope: 'Read docs', state: 'Chờ duyệt' }, - { app: 'GitHub', url: 'https://github.com/rkix/nova-commerce', scope: 'Create branch + PR', state: 'Đã cấp quyền' }, - { app: 'Vercel', url: 'https://vercel.com', scope: 'Deploy preview', state: 'Đã cấp quyền' }, -]; - export function ToolHub() { - const tabs = ['Terminal / Console', 'Configuration', 'Knowledge Base', 'Prompt Editor', 'Monitoring & Metrics', 'Testing / Playground']; return (

Tool Hub

Tiện ích AI Agent Dev

-
{tabs.map((tab, index) => )}
+
{utilityTabs.map((tab) => )}
); } @@ -37,6 +31,7 @@ export function DeploymentDashboard() { return (

Deploy Setup

Cấu hình triển khai tự động

+
{deployTargets.map((target) =>
{target.environment}{target.status}{target.url}
)}
  • Đang kiểm tra kết nối
  • Đang cài đặt phụ thuộc
  • Đang cấu hình server
  • Preview URL đã sẵn sàng
@@ -48,7 +43,7 @@ export function AuthorizationCenter() { return (
🔐

Authorization

Phê duyệt quyền AI Agent

- {authRequests.map((request) =>

{request.app}

{request.url}

{request.scope}
{request.state}
)} + {authRequests.map((request) =>

{request.app}

{request.url}

{request.agent} · {request.scope}
{request.state}
)}
); } @@ -57,7 +52,7 @@ export function AnalyticsDashboard() { return (
📊

Realtime Analytics

Hiệu suất Agent

-
Tasks128
PRs24
Success94%
Cost$8.42
+
{analyticsCards.map(([label, value, delta]) =>
{label}{value}{delta}
)}
Token trend
Task throughput
Workflow mix
); diff --git a/apps/web/components/workspace.tsx b/apps/web/components/workspace.tsx index 823395f..64258a3 100644 --- a/apps/web/components/workspace.tsx +++ b/apps/web/components/workspace.tsx @@ -1,25 +1,5 @@ -import type { AgentTask, ChatMessage, RepositorySummary } from '@codexpr/shared'; import { AgentMark, Glyph } from './icons'; - -const repositories: RepositorySummary[] = [ - { id: 'repo-1', name: 'nova-commerce', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/checkout-flow', status: 'ready' }, - { id: 'repo-2', name: 'booking-agent', provider: 'github', defaultBranch: 'main', activeBranch: 'agent/calendar-ui', status: 'indexing' }, - { id: 'repo-3', name: 'data-portal', provider: 'github', defaultBranch: 'main', status: 'connected' }, -]; - -const messages: ChatMessage[] = [ - { id: 'm1', role: 'user', content: 'Tạo một trang bán giày có thanh toán và dashboard.', createdAt: '09:20' }, - { id: 'm2', role: 'agent', content: 'Gợi ý tối ưu: storefront, checkout, inventory, analytics. Vui lòng xác nhận để bắt đầu build MVP.', createdAt: '09:21', suggestions: ['Storefront', 'Checkout', 'Inventory', 'Analytics'] }, - { id: 'm3', role: 'user', content: 'Xác nhận. Ưu tiên giao diện cao cấp và live preview.', createdAt: '09:22' }, -]; - -const tasks: AgentTask[] = [ - { id: 't1', title: 'Clone repository', status: 'succeeded', progress: 100, logs: ['git clone complete'] }, - { id: 't2', title: 'Install dependencies', status: 'succeeded', progress: 100, logs: ['npm install complete'] }, - { id: 't3', title: 'Edit code', status: 'running', progress: 68, logs: ['writing checkout components'] }, - { id: 't4', title: 'Run tests', status: 'queued', progress: 0, logs: ['waiting for edit step'] }, - { id: 't5', title: 'Create Pull Request', status: 'queued', progress: 0, logs: ['waiting for checks'] }, -]; +import { messages, repositories, tasks } from './data'; export function Sidebar() { return ( diff --git a/package.json b/package.json index 1aca195..7b3761f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "build:web": "next build apps/web", "build:api": "tsc -p apps/api/tsconfig.json", "lint": "node scripts/validate-structure.mjs", - "typecheck": "tsc --noEmit -p tsconfig.json" + "typecheck": "tsc --noEmit -p tsconfig.json", + "deploy:vercel": "vercel --prod" }, "dependencies": { "@fastify/cors": "latest", diff --git a/scripts/validate-structure.mjs b/scripts/validate-structure.mjs index 149ad00..20253d3 100644 --- a/scripts/validate-structure.mjs +++ b/scripts/validate-structure.mjs @@ -2,20 +2,34 @@ import { readFile, stat } from 'node:fs/promises'; const requiredFiles = [ 'apps/web/app/page.tsx', + 'apps/web/app/dashboard/page.tsx', + 'apps/web/app/workplace/page.tsx', + 'apps/web/app/tool-hub/page.tsx', + 'apps/web/app/run-management/page.tsx', + 'apps/web/app/deploy/page.tsx', + 'apps/web/app/auth-center/page.tsx', + 'apps/web/app/analytics/page.tsx', + 'apps/web/app/settings/page.tsx', 'apps/web/app/layout.tsx', 'apps/web/app/globals.css', 'apps/web/components/workspace.tsx', 'apps/web/components/operations.tsx', 'apps/api/src/server.ts', 'apps/api/src/agent-service.ts', + 'apps/api/src/deployment-service.ts', + 'apps/api/src/auth-service.ts', + 'apps/api/src/analytics-service.ts', 'packages/shared/src/index.ts', + 'vercel.json', ]; const requiredTokens = new Map([ ['apps/web/app/page.tsx', ['RepositoryPanel', 'ChatPanel', 'TaskLogPanel', 'Architecture']], - ['apps/web/components/workspace.tsx', ['Clone repository', 'Install dependencies', 'Create Pull Request', 'Thinking']], + ['apps/web/components/workspace.tsx', ['Thinking', 'TaskLogPanel', 'AI Dev Agent']], + ['apps/web/components/data.ts', ['Clone repository', 'Install dependencies', 'Create Pull Request']], ['apps/web/components/operations.tsx', ['Run Management', 'Deploy Setup', 'Authorization', 'Realtime Analytics']], - ['apps/api/src/server.ts', ['/chat', '/tasks/build', '/pull-requests/draft', '/logs/stream']], + ['apps/api/src/server.ts', ['/chat', '/tasks/build', '/pull-requests/draft', '/deploy/setup', '/auth/requests', '/analytics/summary', '/logs/stream']], + ['vercel.json', ['buildCommand', 'npm run build:web', 'framework']], ]); for (const file of requiredFiles) await stat(file); diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..6ddc20e --- /dev/null +++ b/vercel.json @@ -0,0 +1,7 @@ +{ + "framework": "nextjs", + "buildCommand": "npm run build:web", + "devCommand": "npm run dev:web", + "installCommand": "npm install", + "outputDirectory": "apps/web/.next" +} From 20a8453d11b52b2a46bf8a81686f8acb9a1704f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 03:46:38 +0700 Subject: [PATCH 03/14] Fix shared workspace scaffold --- .gitignore | 3 +++ packages/shared/package.json | 8 ++++++++ packages/shared/src/index.ts | 39 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 3 ++- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 packages/shared/package.json create mode 100644 packages/shared/src/index.ts diff --git a/.gitignore b/.gitignore index b5da811..832fa39 100644 --- a/.gitignore +++ b/.gitignore @@ -228,6 +228,9 @@ PublishScripts/ *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* +!packages/ +!packages/shared/ +!packages/shared/** # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..ae53287 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,8 @@ +{ + "name": "@codexpr/shared", + "private": true, + "version": "0.2.0", + "type": "module", + "main": "src/index.ts", + "types": "src/index.ts" +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..a60408c --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,39 @@ +export type RepositoryProvider = 'github' | 'gitlab' | 'bitbucket'; + +export type RepositoryStatus = 'ready' | 'connected' | 'indexing' | 'cloning' | 'building' | 'error'; + +export interface RepositorySummary { + id: string; + name: string; + provider: RepositoryProvider; + defaultBranch: string; + activeBranch: string; + status: RepositoryStatus; +} + +export type ChatRole = 'user' | 'agent' | 'system'; + +export interface ChatMessage { + id: string; + role: ChatRole; + content: string; + createdAt: string; + suggestions?: string[]; +} + +export type AgentTaskStatus = 'queued' | 'running' | 'succeeded' | 'failed'; + +export interface AgentTask { + id: string; + title: string; + status: AgentTaskStatus; + progress: number; + logs: string[]; +} + +export interface PullRequestDraft { + repository: string; + branch: string; + title: string; + checks: string[]; +} diff --git a/tsconfig.json b/tsconfig.json index f509d91..482069f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "paths": { "@codexpr/shared": ["packages/shared/src/index.ts"], "@/*": ["apps/web/*"] - } + }, + "ignoreDeprecations": "6.0" }, "include": ["apps/**/*.ts", "apps/**/*.tsx", "packages/**/*.ts"], "exclude": ["node_modules", "dist", ".next"] From 6f0502e1f2846ae66e6bdf28ba4d3b8341a8e25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 03:46:41 +0700 Subject: [PATCH 04/14] Fix shared workspace scaffold --- .gitignore | 3 +++ packages/shared/package.json | 8 ++++++++ packages/shared/src/index.ts | 39 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 3 ++- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 packages/shared/package.json create mode 100644 packages/shared/src/index.ts diff --git a/.gitignore b/.gitignore index b5da811..832fa39 100644 --- a/.gitignore +++ b/.gitignore @@ -228,6 +228,9 @@ PublishScripts/ *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* +!packages/ +!packages/shared/ +!packages/shared/** # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..ae53287 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,8 @@ +{ + "name": "@codexpr/shared", + "private": true, + "version": "0.2.0", + "type": "module", + "main": "src/index.ts", + "types": "src/index.ts" +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..a60408c --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,39 @@ +export type RepositoryProvider = 'github' | 'gitlab' | 'bitbucket'; + +export type RepositoryStatus = 'ready' | 'connected' | 'indexing' | 'cloning' | 'building' | 'error'; + +export interface RepositorySummary { + id: string; + name: string; + provider: RepositoryProvider; + defaultBranch: string; + activeBranch: string; + status: RepositoryStatus; +} + +export type ChatRole = 'user' | 'agent' | 'system'; + +export interface ChatMessage { + id: string; + role: ChatRole; + content: string; + createdAt: string; + suggestions?: string[]; +} + +export type AgentTaskStatus = 'queued' | 'running' | 'succeeded' | 'failed'; + +export interface AgentTask { + id: string; + title: string; + status: AgentTaskStatus; + progress: number; + logs: string[]; +} + +export interface PullRequestDraft { + repository: string; + branch: string; + title: string; + checks: string[]; +} diff --git a/tsconfig.json b/tsconfig.json index f509d91..482069f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "paths": { "@codexpr/shared": ["packages/shared/src/index.ts"], "@/*": ["apps/web/*"] - } + }, + "ignoreDeprecations": "6.0" }, "include": ["apps/**/*.ts", "apps/**/*.tsx", "packages/**/*.ts"], "exclude": ["node_modules", "dist", ".next"] From 62b68a87cd678ecc2031ee1507a73686bc04ac0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 04:19:11 +0700 Subject: [PATCH 05/14] Merge pull request #16 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m-ixlftb Merge pull request #13 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m Merge pull request #12 from HuynhThuong0711/codex/giai-thich-ma-goc-cho-nguoi-moi-7lss8n Merge pull request #4 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m-ixlftb fix: add @codexpr/shared workspace and adjust config for scaffold validation (#17) From 06a3bbab8d6f5abc978acea3103a0bdabc14ad5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Fri, 3 Jul 2026 04:24:55 +0700 Subject: [PATCH 06/14] Merge pull request #16 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m-ixlftb (#20) Merge pull request #13 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m Merge pull request #12 from HuynhThuong0711/codex/giai-thich-ma-goc-cho-nguoi-moi-7lss8n Merge pull request #4 from HuynhThuong0711/codex/github-mention-scaffold-add-web-next.js-app,-api-fastify-m-ixlftb fix: add @codexpr/shared workspace and adjust config for scaffold validation (#17) From cff16d7d6c8b74e367d3a7c5e2ee299746a696b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:42:12 +0700 Subject: [PATCH 07/14] Update packages/shared/src/index.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- packages/shared/src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index a60408c..e5c5307 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -37,3 +37,9 @@ export interface PullRequestDraft { title: string; checks: string[]; } + +export interface DeployTarget { + environment: 'production' | 'staging'; + status: 'processing' | 'complete' | 'error'; + url: string; +} From c323bf29ac53ea682954092fc7322a843c08dc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:42:37 +0700 Subject: [PATCH 08/14] Update apps/api/src/server.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 0a0559f..060369d 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -60,4 +60,4 @@ server.get('/logs/stream', { websocket: true }, (connection) => { connection.socket.on('close', () => clearInterval(timer)); }); -server.listen({ port: config.port, host: '0.0.0.0' }); +await server.listen({ port: config.port, host: '0.0.0.0' }); From f2af40441c46b8847252582daabd0fe18fcb8cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:45:39 +0700 Subject: [PATCH 09/14] Update apps/api/tsconfig.json Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 9bd0d84..52c10de 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": "../..", + "rootDir": "src", "noEmit": false, "module": "NodeNext", "moduleResolution": "NodeNext", From d625a50307dfa0b69acf2232a20c3baf49c57caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:49:43 +0700 Subject: [PATCH 10/14] Update apps/api/src/server.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/src/server.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 060369d..4700843 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -49,15 +49,15 @@ server.post('/auth/requests/:id/approve', async (request) => { server.get('/analytics/summary', async () => analyticsService.summary()); -server.get('/logs/stream', { websocket: true }, (connection) => { +server.get('/logs/stream', { websocket: true }, (socket) => { const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; let index = 0; const timer = setInterval(() => { - connection.socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); + socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); index += 1; if (index >= events.length) clearInterval(timer); }, 1000); - connection.socket.on('close', () => clearInterval(timer)); + socket.on('close', () => clearInterval(timer)); }); await server.listen({ port: config.port, host: '0.0.0.0' }); From 4f97c2cf6f528eac9c3e2a74c3dd1b0962e8d8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:50:21 +0700 Subject: [PATCH 11/14] Update packages/shared/src/index.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- packages/shared/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index e5c5307..b24ccf4 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -7,7 +7,7 @@ export interface RepositorySummary { name: string; provider: RepositoryProvider; defaultBranch: string; - activeBranch: string; + activeBranch?: string; status: RepositoryStatus; } From a6306512ba4c4a93e122e569d5a7552671c5073d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:51:33 +0700 Subject: [PATCH 12/14] Update apps/api/src/server.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/src/server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 4700843..8022a58 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -53,7 +53,8 @@ server.get('/logs/stream', { websocket: true }, (socket) => { const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; let index = 0; const timer = setInterval(() => { - socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); + if (connection.socket.readyState !== 1) { clearInterval(timer); return; } + connection.socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); index += 1; if (index >= events.length) clearInterval(timer); }, 1000); From f982e94c04578121fad02cab3049e2c7116b7da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 04:52:10 +0700 Subject: [PATCH 13/14] Update apps/api/src/server.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/src/server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 8022a58..8523757 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -52,6 +52,7 @@ server.get('/analytics/summary', async () => analyticsService.summary()); server.get('/logs/stream', { websocket: true }, (socket) => { const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; let index = 0; + const timer = setInterval(() => { const timer = setInterval(() => { if (connection.socket.readyState !== 1) { clearInterval(timer); return; } connection.socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); From 1a7519cad7a46e79c3a1bb472245217b303a1660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0ong?= <299259079+HuynhThuong0711@users.noreply.github.com> Date: Mon, 6 Jul 2026 05:20:08 +0700 Subject: [PATCH 14/14] Update apps/api/src/server.ts Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/api/src/server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 8523757..8c2eb8f 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -53,9 +53,8 @@ server.get('/logs/stream', { websocket: true }, (socket) => { const events = ['Clone Repository', 'Install', 'Edit code', 'Run tests', 'Commit', 'Push branch', 'Create Pull Request']; let index = 0; const timer = setInterval(() => { - const timer = setInterval(() => { - if (connection.socket.readyState !== 1) { clearInterval(timer); return; } - connection.socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); + if (socket.readyState !== 1) { clearInterval(timer); return; } + socket.send(JSON.stringify({ event: events[index], at: new Date().toISOString() })); index += 1; if (index >= events.length) clearInterval(timer); }, 1000);