diff --git a/.dockerignore b/.dockerignore index a51fe5e..8e5297f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ .git .next +apps/*/.next coverage node_modules Dockerfile @@ -10,3 +11,4 @@ docker-compose.yml *.log *.tsbuildinfo generated/prisma +apps/*/generated/prisma diff --git a/.gitignore b/.gitignore index ac80358..a4c4faf 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,11 @@ pnpm-debug.log* /prisma/dev.db /prisma/dev.db-journal /generated/prisma +/apps/web/generated/prisma # typescript *.tsbuildinfo next-env.d.ts + +# workspace package installs +node_modules/ diff --git a/Dockerfile b/Dockerfile index 5e1711b..1620b6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,17 +10,23 @@ WORKDIR /app FROM base AS deps COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY apps/web/package.json ./apps/web/package.json +COPY packages/cli/package.json ./packages/cli/package.json RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile FROM base AS builder ENV AUTH_SECRET="docker-build-placeholder" ENV DATABASE_URL="postgresql://agentbridge:agentbridge@localhost:5432/agentbridge?schema=public" COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules COPY . . -RUN pnpm build +RUN pnpm prisma generate +RUN pnpm build:web FROM base AS prod-deps COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY apps/web/package.json ./apps/web/package.json +COPY packages/cli/package.json ./packages/cli/package.json RUN --mount=type=cache,id=pnpm-prod,target=/pnpm/store pnpm install --frozen-lockfile --prod FROM base AS runner @@ -32,11 +38,14 @@ RUN addgroup --system --gid 1001 nodejs \ && adduser --system --uid 1001 nextjs COPY --from=prod-deps --chown=nextjs:nodejs /app/node_modules ./node_modules -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -COPY --from=builder --chown=nextjs:nodejs /app/generated ./generated -COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next ./apps/web/.next +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/generated ./apps/web/generated COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma +COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json +COPY --from=builder --chown=nextjs:nodejs /app/pnpm-workspace.yaml ./pnpm-workspace.yaml +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/package.json ./apps/web/package.json +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/next.config.mjs ./apps/web/next.config.mjs COPY --from=builder --chown=nextjs:nodejs /app/prisma.config.ts ./prisma.config.ts COPY --from=builder --chown=nextjs:nodejs /app/docker-entrypoint.sh ./docker-entrypoint.sh @@ -47,4 +56,4 @@ USER nextjs EXPOSE 3000 ENTRYPOINT ["./docker-entrypoint.sh"] -CMD ["node", "server.js"] +CMD ["pnpm", "--filter", "@agentbridge/web", "start", "--", "-H", "0.0.0.0"] diff --git a/README.md b/README.md index 06453d9..435d166 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ Another fun detail: **Kaito is currently the most frequent code pusher in this r ## Preview -![AgentBridge project dashboard preview](public/readme/agentbridge-preview-1.jpg) +![AgentBridge project dashboard preview](apps/web/public/readme/agentbridge-preview-1.jpg) -![AgentBridge task board preview](public/readme/agentbridge-preview-2.jpg) +![AgentBridge task board preview](apps/web/public/readme/agentbridge-preview-2.jpg) -![AgentBridge agent coordination preview](public/readme/agentbridge-preview-3.jpg) +![AgentBridge agent coordination preview](apps/web/public/readme/agentbridge-preview-3.jpg) ## Current capabilities @@ -180,7 +180,7 @@ You can generate a new company bearer token later from dashboard company setting ## OpenClaw setup with the CLI -The repository includes a publish-ready `cli/` workspace package for setting up AgentBridge in OpenClaw workspaces. +The repository includes a publish-ready `packages/cli/` workspace package for setting up AgentBridge in OpenClaw workspaces. After the CLI is published to npm, the intended install-free usage is: @@ -311,6 +311,15 @@ Useful Agent API resources: - The current implementation exposes dashboard read-review state through task read marker fields documented in `/api/openapi` and `agent-skill/SKILL.md`. - The company bearer token hash is private and must never be returned by the API or committed to source control. +## Repository layout + +AgentBridge is organized as a pnpm workspace: + +- `apps/web/` contains the Next.js dashboard and API routes. +- `packages/cli/` contains the publishable `agentbridge` CLI package. +- `prisma/` and Prisma scripts stay at the repository root; Prisma client output is generated into `apps/web/generated/prisma`. +- Root `package.json` scripts orchestrate workspace commands with pnpm filters. + ## Development commands Run these from the repository root: @@ -329,8 +338,11 @@ corepack pnpm prisma:generate corepack pnpm prisma:migrate corepack pnpm prisma:studio corepack pnpm format +corepack pnpm build:web corepack pnpm cli:dev -- init --every 1h +corepack pnpm cli:typecheck corepack pnpm cli:build +corepack pnpm cli:pack ``` For contribution conventions, branch expectations, and QA checklist, see [CONTRIBUTING.md](CONTRIBUTING.md). @@ -366,7 +378,7 @@ For production-like environments: 1. Provide a managed PostgreSQL `DATABASE_URL`. 2. Set a strong `AUTH_SECRET`. 3. Run migrations with `prisma migrate deploy` as part of release startup or deployment automation. -4. Generate Prisma client code before building, or use the existing `build` script. +4. Generate Prisma client code before building, or use the existing root `build`/`build:web` scripts. 5. Seed or create the first operator account through an approved operational process. 6. Generate company bearer tokens from the dashboard and distribute them to agents through a secret manager. 7. Monitor `GET /api/health` after deploys. Treat HTTP `200` as ready and HTTP `503` as the app running but not ready because database connectivity failed. diff --git a/app/api/agent/agents/[agentId]/route.ts b/apps/web/app/api/agent/agents/[agentId]/route.ts similarity index 100% rename from app/api/agent/agents/[agentId]/route.ts rename to apps/web/app/api/agent/agents/[agentId]/route.ts diff --git a/app/api/agent/agents/route.ts b/apps/web/app/api/agent/agents/route.ts similarity index 100% rename from app/api/agent/agents/route.ts rename to apps/web/app/api/agent/agents/route.ts diff --git a/app/api/agent/projects/[projectId]/route.ts b/apps/web/app/api/agent/projects/[projectId]/route.ts similarity index 100% rename from app/api/agent/projects/[projectId]/route.ts rename to apps/web/app/api/agent/projects/[projectId]/route.ts diff --git a/app/api/agent/projects/route.ts b/apps/web/app/api/agent/projects/route.ts similarity index 100% rename from app/api/agent/projects/route.ts rename to apps/web/app/api/agent/projects/route.ts diff --git a/app/api/agent/route.ts b/apps/web/app/api/agent/route.ts similarity index 100% rename from app/api/agent/route.ts rename to apps/web/app/api/agent/route.ts diff --git a/app/api/agent/tasks/[taskId]/route.ts b/apps/web/app/api/agent/tasks/[taskId]/route.ts similarity index 99% rename from app/api/agent/tasks/[taskId]/route.ts rename to apps/web/app/api/agent/tasks/[taskId]/route.ts index df5fb1a..5148ce5 100644 --- a/app/api/agent/tasks/[taskId]/route.ts +++ b/apps/web/app/api/agent/tasks/[taskId]/route.ts @@ -350,7 +350,7 @@ export async function PATCH(request: NextRequest, { params }: RouteContext) { data.summaryUpdatedAt = data.note ? new Date() : null } const shouldClearNextStatusReads = !hasReadBy && (statusChanged || noteChanged) - const updatedTask = await prisma.$transaction(async (tx) => { + const updatedTask = await prisma.$transaction(async (tx: typeof prisma) => { if (hasReadBy) { await tx.taskReadMarker.deleteMany({ where: { diff --git a/app/api/agent/tasks/route.ts b/apps/web/app/api/agent/tasks/route.ts similarity index 99% rename from app/api/agent/tasks/route.ts rename to apps/web/app/api/agent/tasks/route.ts index 5019e5e..713d346 100644 --- a/app/api/agent/tasks/route.ts +++ b/apps/web/app/api/agent/tasks/route.ts @@ -297,7 +297,7 @@ export async function POST(request: NextRequest) { ) } - const task = await prisma.$transaction(async (tx) => { + const task = await prisma.$transaction(async (tx: typeof prisma) => { await tx.projectAgent.createMany({ data: [{ projectId: project.id, agentId: assignedAgentId }], skipDuplicates: true, diff --git a/app/api/health/route.ts b/apps/web/app/api/health/route.ts similarity index 100% rename from app/api/health/route.ts rename to apps/web/app/api/health/route.ts diff --git a/app/api/internal/account/password/route.ts b/apps/web/app/api/internal/account/password/route.ts similarity index 100% rename from app/api/internal/account/password/route.ts rename to apps/web/app/api/internal/account/password/route.ts diff --git a/app/api/internal/account/username/route.ts b/apps/web/app/api/internal/account/username/route.ts similarity index 100% rename from app/api/internal/account/username/route.ts rename to apps/web/app/api/internal/account/username/route.ts diff --git a/app/api/internal/agents/[agentId]/route.ts b/apps/web/app/api/internal/agents/[agentId]/route.ts similarity index 100% rename from app/api/internal/agents/[agentId]/route.ts rename to apps/web/app/api/internal/agents/[agentId]/route.ts diff --git a/app/api/internal/agents/route.ts b/apps/web/app/api/internal/agents/route.ts similarity index 100% rename from app/api/internal/agents/route.ts rename to apps/web/app/api/internal/agents/route.ts diff --git a/app/api/internal/audit-logs/route.ts b/apps/web/app/api/internal/audit-logs/route.ts similarity index 100% rename from app/api/internal/audit-logs/route.ts rename to apps/web/app/api/internal/audit-logs/route.ts diff --git a/app/api/internal/companies/[companyId]/route.ts b/apps/web/app/api/internal/companies/[companyId]/route.ts similarity index 100% rename from app/api/internal/companies/[companyId]/route.ts rename to apps/web/app/api/internal/companies/[companyId]/route.ts diff --git a/app/api/internal/companies/route.ts b/apps/web/app/api/internal/companies/route.ts similarity index 100% rename from app/api/internal/companies/route.ts rename to apps/web/app/api/internal/companies/route.ts diff --git a/app/api/internal/dashboard/brief/route.ts b/apps/web/app/api/internal/dashboard/brief/route.ts similarity index 100% rename from app/api/internal/dashboard/brief/route.ts rename to apps/web/app/api/internal/dashboard/brief/route.ts diff --git a/app/api/internal/dashboard/summary/route.ts b/apps/web/app/api/internal/dashboard/summary/route.ts similarity index 100% rename from app/api/internal/dashboard/summary/route.ts rename to apps/web/app/api/internal/dashboard/summary/route.ts diff --git a/app/api/internal/notes/[taskId]/read/route.ts b/apps/web/app/api/internal/notes/[taskId]/read/route.ts similarity index 100% rename from app/api/internal/notes/[taskId]/read/route.ts rename to apps/web/app/api/internal/notes/[taskId]/read/route.ts diff --git a/app/api/internal/notes/route.ts b/apps/web/app/api/internal/notes/route.ts similarity index 100% rename from app/api/internal/notes/route.ts rename to apps/web/app/api/internal/notes/route.ts diff --git a/app/api/internal/projects/[projectId]/agents/route.ts b/apps/web/app/api/internal/projects/[projectId]/agents/route.ts similarity index 97% rename from app/api/internal/projects/[projectId]/agents/route.ts rename to apps/web/app/api/internal/projects/[projectId]/agents/route.ts index d9963aa..bca31eb 100644 --- a/app/api/internal/projects/[projectId]/agents/route.ts +++ b/apps/web/app/api/internal/projects/[projectId]/agents/route.ts @@ -69,7 +69,7 @@ export async function PUT(request: Request, { params }: RouteContext) { .filter((agent) => !previousAgentIds.has(agent.id)) .map((agent) => agent.name) - const projectAgents = await prisma.$transaction(async (tx) => { + const projectAgents = await prisma.$transaction(async (tx: typeof prisma) => { await tx.projectAgent.deleteMany({ where: { projectId: project.id, diff --git a/app/api/internal/projects/[projectId]/archive-done/route.ts b/apps/web/app/api/internal/projects/[projectId]/archive-done/route.ts similarity index 100% rename from app/api/internal/projects/[projectId]/archive-done/route.ts rename to apps/web/app/api/internal/projects/[projectId]/archive-done/route.ts diff --git a/app/api/internal/projects/[projectId]/route.ts b/apps/web/app/api/internal/projects/[projectId]/route.ts similarity index 100% rename from app/api/internal/projects/[projectId]/route.ts rename to apps/web/app/api/internal/projects/[projectId]/route.ts diff --git a/app/api/internal/projects/route.ts b/apps/web/app/api/internal/projects/route.ts similarity index 100% rename from app/api/internal/projects/route.ts rename to apps/web/app/api/internal/projects/route.ts diff --git a/app/api/internal/tasks/[taskId]/route.ts b/apps/web/app/api/internal/tasks/[taskId]/route.ts similarity index 99% rename from app/api/internal/tasks/[taskId]/route.ts rename to apps/web/app/api/internal/tasks/[taskId]/route.ts index f613c55..18d088d 100644 --- a/app/api/internal/tasks/[taskId]/route.ts +++ b/apps/web/app/api/internal/tasks/[taskId]/route.ts @@ -249,7 +249,7 @@ export async function PATCH(request: Request, { params }: RouteContext) { const statusChanged = nextStatus !== task.status const noteChanged = note !== undefined && (note || null) !== task.note const shouldClearNextStatusReads = !hasReadByAgentIds && (statusChanged || noteChanged) - const updatedTask = await prisma.$transaction(async (tx) => { + const updatedTask = await prisma.$transaction(async (tx: typeof prisma) => { if (hasReadByAgentIds) { await tx.taskReadMarker.deleteMany({ where: { diff --git a/app/api/internal/tasks/route.ts b/apps/web/app/api/internal/tasks/route.ts similarity index 100% rename from app/api/internal/tasks/route.ts rename to apps/web/app/api/internal/tasks/route.ts diff --git a/app/api/openapi/route.ts b/apps/web/app/api/openapi/route.ts similarity index 99% rename from app/api/openapi/route.ts rename to apps/web/app/api/openapi/route.ts index 6b557c4..5194b46 100644 --- a/app/api/openapi/route.ts +++ b/apps/web/app/api/openapi/route.ts @@ -405,7 +405,7 @@ const openApiDocument = swaggerJsdoc({ }, security: [{ bearerAuth: [] }], }, - apis: ["./app/api/agent/**/*.ts", "./app/api/agent/route.ts"], + apis: ["./apps/web/app/api/agent/**/*.ts", "./apps/web/app/api/agent/route.ts"], }) export function GET() { diff --git a/app/api/swagger/route.ts b/apps/web/app/api/swagger/route.ts similarity index 100% rename from app/api/swagger/route.ts rename to apps/web/app/api/swagger/route.ts diff --git a/app/dashboard/agents/agent-row-actions.tsx b/apps/web/app/dashboard/agents/agent-row-actions.tsx similarity index 100% rename from app/dashboard/agents/agent-row-actions.tsx rename to apps/web/app/dashboard/agents/agent-row-actions.tsx diff --git a/app/dashboard/agents/agents-table.tsx b/apps/web/app/dashboard/agents/agents-table.tsx similarity index 100% rename from app/dashboard/agents/agents-table.tsx rename to apps/web/app/dashboard/agents/agents-table.tsx diff --git a/app/dashboard/agents/page.tsx b/apps/web/app/dashboard/agents/page.tsx similarity index 100% rename from app/dashboard/agents/page.tsx rename to apps/web/app/dashboard/agents/page.tsx diff --git a/app/dashboard/audit-logs/audit-log-list.tsx b/apps/web/app/dashboard/audit-logs/audit-log-list.tsx similarity index 100% rename from app/dashboard/audit-logs/audit-log-list.tsx rename to apps/web/app/dashboard/audit-logs/audit-log-list.tsx diff --git a/app/dashboard/audit-logs/page.tsx b/apps/web/app/dashboard/audit-logs/page.tsx similarity index 100% rename from app/dashboard/audit-logs/page.tsx rename to apps/web/app/dashboard/audit-logs/page.tsx diff --git a/app/dashboard/brief/brief-client.tsx b/apps/web/app/dashboard/brief/brief-client.tsx similarity index 100% rename from app/dashboard/brief/brief-client.tsx rename to apps/web/app/dashboard/brief/brief-client.tsx diff --git a/app/dashboard/brief/page.tsx b/apps/web/app/dashboard/brief/page.tsx similarity index 100% rename from app/dashboard/brief/page.tsx rename to apps/web/app/dashboard/brief/page.tsx diff --git a/app/dashboard/dashboard-summary.tsx b/apps/web/app/dashboard/dashboard-summary.tsx similarity index 100% rename from app/dashboard/dashboard-summary.tsx rename to apps/web/app/dashboard/dashboard-summary.tsx diff --git a/app/dashboard/docs/page.tsx b/apps/web/app/dashboard/docs/page.tsx similarity index 100% rename from app/dashboard/docs/page.tsx rename to apps/web/app/dashboard/docs/page.tsx diff --git a/app/dashboard/notes/note-list.tsx b/apps/web/app/dashboard/notes/note-list.tsx similarity index 100% rename from app/dashboard/notes/note-list.tsx rename to apps/web/app/dashboard/notes/note-list.tsx diff --git a/app/dashboard/notes/page.tsx b/apps/web/app/dashboard/notes/page.tsx similarity index 100% rename from app/dashboard/notes/page.tsx rename to apps/web/app/dashboard/notes/page.tsx diff --git a/app/dashboard/page.tsx b/apps/web/app/dashboard/page.tsx similarity index 100% rename from app/dashboard/page.tsx rename to apps/web/app/dashboard/page.tsx diff --git a/app/dashboard/projects/[projectId]/create-task-dialog.tsx b/apps/web/app/dashboard/projects/[projectId]/create-task-dialog.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/create-task-dialog.tsx rename to apps/web/app/dashboard/projects/[projectId]/create-task-dialog.tsx diff --git a/app/dashboard/projects/[projectId]/page.tsx b/apps/web/app/dashboard/projects/[projectId]/page.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/page.tsx rename to apps/web/app/dashboard/projects/[projectId]/page.tsx diff --git a/app/dashboard/projects/[projectId]/project-agents-manager.tsx b/apps/web/app/dashboard/projects/[projectId]/project-agents-manager.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/project-agents-manager.tsx rename to apps/web/app/dashboard/projects/[projectId]/project-agents-manager.tsx diff --git a/app/dashboard/projects/[projectId]/project-detail-client.tsx b/apps/web/app/dashboard/projects/[projectId]/project-detail-client.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/project-detail-client.tsx rename to apps/web/app/dashboard/projects/[projectId]/project-detail-client.tsx diff --git a/app/dashboard/projects/[projectId]/project-overview.tsx b/apps/web/app/dashboard/projects/[projectId]/project-overview.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/project-overview.tsx rename to apps/web/app/dashboard/projects/[projectId]/project-overview.tsx diff --git a/app/dashboard/projects/[projectId]/task-kanban.tsx b/apps/web/app/dashboard/projects/[projectId]/task-kanban.tsx similarity index 100% rename from app/dashboard/projects/[projectId]/task-kanban.tsx rename to apps/web/app/dashboard/projects/[projectId]/task-kanban.tsx diff --git a/app/dashboard/projects/[projectId]/types.ts b/apps/web/app/dashboard/projects/[projectId]/types.ts similarity index 100% rename from app/dashboard/projects/[projectId]/types.ts rename to apps/web/app/dashboard/projects/[projectId]/types.ts diff --git a/app/dashboard/projects/create-project-dialog.tsx b/apps/web/app/dashboard/projects/create-project-dialog.tsx similarity index 100% rename from app/dashboard/projects/create-project-dialog.tsx rename to apps/web/app/dashboard/projects/create-project-dialog.tsx diff --git a/app/dashboard/projects/page.tsx b/apps/web/app/dashboard/projects/page.tsx similarity index 100% rename from app/dashboard/projects/page.tsx rename to apps/web/app/dashboard/projects/page.tsx diff --git a/app/dashboard/projects/projects-list.tsx b/apps/web/app/dashboard/projects/projects-list.tsx similarity index 100% rename from app/dashboard/projects/projects-list.tsx rename to apps/web/app/dashboard/projects/projects-list.tsx diff --git a/app/dashboard/settings/company-settings-form.tsx b/apps/web/app/dashboard/settings/company-settings-form.tsx similarity index 100% rename from app/dashboard/settings/company-settings-form.tsx rename to apps/web/app/dashboard/settings/company-settings-form.tsx diff --git a/app/dashboard/settings/page.tsx b/apps/web/app/dashboard/settings/page.tsx similarity index 100% rename from app/dashboard/settings/page.tsx rename to apps/web/app/dashboard/settings/page.tsx diff --git a/app/favicon.ico b/apps/web/app/favicon.ico similarity index 100% rename from app/favicon.ico rename to apps/web/app/favicon.ico diff --git a/app/globals.css b/apps/web/app/globals.css similarity index 100% rename from app/globals.css rename to apps/web/app/globals.css diff --git a/app/layout.tsx b/apps/web/app/layout.tsx similarity index 100% rename from app/layout.tsx rename to apps/web/app/layout.tsx diff --git a/app/login/actions.ts b/apps/web/app/login/actions.ts similarity index 100% rename from app/login/actions.ts rename to apps/web/app/login/actions.ts diff --git a/app/login/login-form.tsx b/apps/web/app/login/login-form.tsx similarity index 100% rename from app/login/login-form.tsx rename to apps/web/app/login/login-form.tsx diff --git a/app/login/page.tsx b/apps/web/app/login/page.tsx similarity index 100% rename from app/login/page.tsx rename to apps/web/app/login/page.tsx diff --git a/app/logout/route.ts b/apps/web/app/logout/route.ts similarity index 100% rename from app/logout/route.ts rename to apps/web/app/logout/route.ts diff --git a/app/page.tsx b/apps/web/app/page.tsx similarity index 100% rename from app/page.tsx rename to apps/web/app/page.tsx diff --git a/components.json b/apps/web/components.json similarity index 100% rename from components.json rename to apps/web/components.json diff --git a/components/.gitkeep b/apps/web/components/.gitkeep similarity index 100% rename from components/.gitkeep rename to apps/web/components/.gitkeep diff --git a/components/dashboard/change-password-dialog.tsx b/apps/web/components/dashboard/change-password-dialog.tsx similarity index 100% rename from components/dashboard/change-password-dialog.tsx rename to apps/web/components/dashboard/change-password-dialog.tsx diff --git a/components/dashboard/change-username-dialog.tsx b/apps/web/components/dashboard/change-username-dialog.tsx similarity index 100% rename from components/dashboard/change-username-dialog.tsx rename to apps/web/components/dashboard/change-username-dialog.tsx diff --git a/components/dashboard/create-company-dialog.tsx b/apps/web/components/dashboard/create-company-dialog.tsx similarity index 100% rename from components/dashboard/create-company-dialog.tsx rename to apps/web/components/dashboard/create-company-dialog.tsx diff --git a/components/dashboard/shell.tsx b/apps/web/components/dashboard/shell.tsx similarity index 100% rename from components/dashboard/shell.tsx rename to apps/web/components/dashboard/shell.tsx diff --git a/components/dashboard/sidebar.tsx b/apps/web/components/dashboard/sidebar.tsx similarity index 100% rename from components/dashboard/sidebar.tsx rename to apps/web/components/dashboard/sidebar.tsx diff --git a/components/query-provider.tsx b/apps/web/components/query-provider.tsx similarity index 100% rename from components/query-provider.tsx rename to apps/web/components/query-provider.tsx diff --git a/components/theme-provider.tsx b/apps/web/components/theme-provider.tsx similarity index 87% rename from components/theme-provider.tsx rename to apps/web/components/theme-provider.tsx index 3d44f48..25f9f30 100644 --- a/components/theme-provider.tsx +++ b/apps/web/components/theme-provider.tsx @@ -1,12 +1,13 @@ "use client" import * as React from "react" -import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes" +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, + useTheme, +} from "next-themes" -function ThemeProvider({ - children, - ...props -}: React.ComponentProps) { +function ThemeProvider({ children, ...props }: ThemeProviderProps) { return ( + const globalForPrisma = globalThis as unknown as { - prisma?: PrismaClient + prisma?: AgentBridgePrismaClient } const adapter = new PrismaPg({ connectionString }) diff --git a/lib/token.ts b/apps/web/lib/token.ts similarity index 100% rename from lib/token.ts rename to apps/web/lib/token.ts diff --git a/lib/utils.ts b/apps/web/lib/utils.ts similarity index 100% rename from lib/utils.ts rename to apps/web/lib/utils.ts diff --git a/middleware.ts b/apps/web/middleware.ts similarity index 100% rename from middleware.ts rename to apps/web/middleware.ts diff --git a/next.config.mjs b/apps/web/next.config.mjs similarity index 100% rename from next.config.mjs rename to apps/web/next.config.mjs diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..7a8a72f --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,49 @@ +{ + "name": "@agentbridge/web", + "version": "0.0.1", + "type": "module", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build --webpack", + "start": "next start", + "lint": "eslint", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@prisma/adapter-pg": "^7.8.0", + "@prisma/client": "^7.8.0", + "@tanstack/react-query": "^5.100.9", + "bcryptjs": "^3.0.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "dotenv": "^17.4.2", + "jose": "^6.2.3", + "lucide-react": "^1.14.0", + "next": "16.1.7", + "next-themes": "^0.4.6", + "pg": "^8.20.0", + "radix-ui": "^1.4.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "shadcn": "^4.7.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-dist": "^5.32.5", + "tailwind-merge": "^3.5.0", + "tw-animate-css": "^1.4.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.2.1", + "@types/node": "^25.5.0", + "@types/pg": "^8.20.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/swagger-jsdoc": "^6.0.4", + "eslint": "^9.39.4", + "eslint-config-next": "16.1.7", + "postcss": "^8", + "tailwindcss": "^4.2.1", + "typescript": "^5.9.3" + } +} diff --git a/postcss.config.mjs b/apps/web/postcss.config.mjs similarity index 100% rename from postcss.config.mjs rename to apps/web/postcss.config.mjs diff --git a/public/.gitkeep b/apps/web/public/.gitkeep similarity index 100% rename from public/.gitkeep rename to apps/web/public/.gitkeep diff --git a/public/readme/agentbridge-preview-1.jpg b/apps/web/public/readme/agentbridge-preview-1.jpg similarity index 100% rename from public/readme/agentbridge-preview-1.jpg rename to apps/web/public/readme/agentbridge-preview-1.jpg diff --git a/public/readme/agentbridge-preview-2.jpg b/apps/web/public/readme/agentbridge-preview-2.jpg similarity index 100% rename from public/readme/agentbridge-preview-2.jpg rename to apps/web/public/readme/agentbridge-preview-2.jpg diff --git a/public/readme/agentbridge-preview-3.jpg b/apps/web/public/readme/agentbridge-preview-3.jpg similarity index 100% rename from public/readme/agentbridge-preview-3.jpg rename to apps/web/public/readme/agentbridge-preview-3.jpg diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..88a313a --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,44 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "strictFunctionTypes": false, + "noImplicitAny": false, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "baseUrl": ".", + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/package.json b/package.json index e191ccd..2f4b2ad 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,37 @@ { - "name": "AgentBridge", + "name": "agentbridge-monorepo", "version": "0.0.1", "type": "module", "private": true, "scripts": { - "dev": "next dev --turbopack", - "build": "prisma generate && next build", - "start": "next start", - "lint": "eslint", + "dev": "corepack pnpm --filter @agentbridge/web dev", + "build": "corepack pnpm prisma:generate && corepack pnpm --filter @agentbridge/web build && corepack pnpm --filter agentbridge build", + "build:web": "corepack pnpm prisma:generate && corepack pnpm --filter @agentbridge/web build", + "start": "corepack pnpm --filter @agentbridge/web start", + "lint": "corepack pnpm --filter @agentbridge/web lint", + "lint:web": "corepack pnpm --filter @agentbridge/web lint", "format": "prettier --write \"**/*.{ts,tsx}\"", "prisma:generate": "prisma generate", "prisma:migrate": "prisma migrate dev", "prisma:seed": "tsx scripts/seed-admin.ts", "prisma:studio": "prisma studio", - "pretypecheck": "prisma generate", - "typecheck": "tsc --noEmit", + "typecheck": "corepack pnpm prisma:generate && corepack pnpm --filter @agentbridge/web typecheck && corepack pnpm --filter agentbridge typecheck", + "typecheck:web": "corepack pnpm prisma:generate && corepack pnpm --filter @agentbridge/web typecheck", "cli:dev": "corepack pnpm --filter agentbridge dev", "cli:build": "corepack pnpm --filter agentbridge build", + "cli:typecheck": "corepack pnpm --filter agentbridge typecheck", "cli:pack": "corepack pnpm --filter agentbridge pack:dry-run" }, - "dependencies": { + "devDependencies": { "@prisma/adapter-pg": "^7.8.0", - "@prisma/client": "^7.8.0", - "@tanstack/react-query": "^5.100.9", + "@types/node": "^25.5.0", + "@types/pg": "^8.20.0", "bcryptjs": "^3.0.3", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", "dotenv": "^17.4.2", - "jose": "^6.2.3", - "lucide-react": "^1.14.0", - "next": "16.1.7", - "next-themes": "^0.4.6", "pg": "^8.20.0", - "prisma": "^7.8.0", - "radix-ui": "^1.4.3", - "react": "^19.2.4", - "react-dom": "^19.2.4", - "shadcn": "^4.7.0", - "swagger-jsdoc": "^6.2.8", - "swagger-ui-dist": "^5.32.5", - "tailwind-merge": "^3.5.0", - "tw-animate-css": "^1.4.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@tailwindcss/postcss": "^4.2.1", - "@types/node": "^25.5.0", - "@types/pg": "^8.20.0", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@types/swagger-jsdoc": "^6.0.4", - "eslint": "^9.39.4", - "eslint-config-next": "16.1.7", - "postcss": "^8", "prettier": "^3.8.1", "prettier-plugin-tailwindcss": "^0.7.2", + "prisma": "^7.8.0", "tailwindcss": "^4.2.1", "tsx": "^4.21.0", "typescript": "^5.9.3" diff --git a/cli/.gitignore b/packages/cli/.gitignore similarity index 100% rename from cli/.gitignore rename to packages/cli/.gitignore diff --git a/cli/README.md b/packages/cli/README.md similarity index 97% rename from cli/README.md rename to packages/cli/README.md index 86032d5..1d81671 100644 --- a/cli/README.md +++ b/packages/cli/README.md @@ -36,7 +36,7 @@ Before publish, test the built package locally: ```bash corepack pnpm --filter agentbridge build -node cli/dist/index.js openclaw status --workspace ~/.openclaw +node packages/cli/dist/index.js openclaw status --workspace ~/.openclaw ``` ## Project init vs agent setup diff --git a/cli/package.json b/packages/cli/package.json similarity index 97% rename from cli/package.json rename to packages/cli/package.json index 2760d38..56beeee 100644 --- a/cli/package.json +++ b/packages/cli/package.json @@ -33,7 +33,7 @@ "repository": { "type": "git", "url": "git+https://github.com/Vann-Dev/AgentBridge.git", - "directory": "cli" + "directory": "packages/cli" }, "bugs": { "url": "https://github.com/Vann-Dev/AgentBridge/issues" diff --git a/cli/src/index.ts b/packages/cli/src/index.ts similarity index 100% rename from cli/src/index.ts rename to packages/cli/src/index.ts diff --git a/cli/tsconfig.json b/packages/cli/tsconfig.json similarity index 88% rename from cli/tsconfig.json rename to packages/cli/tsconfig.json index acabc5a..e25096d 100644 --- a/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", "rootDir": "src", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5adb61..cf03473 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,45 @@ settings: importers: .: + devDependencies: + '@prisma/adapter-pg': + specifier: ^7.8.0 + version: 7.8.0 + '@types/node': + specifier: ^25.5.0 + version: 25.6.2 + '@types/pg': + specifier: ^8.20.0 + version: 8.20.0 + bcryptjs: + specifier: ^3.0.3 + version: 3.0.3 + dotenv: + specifier: ^17.4.2 + version: 17.4.2 + pg: + specifier: ^8.20.0 + version: 8.20.0 + prettier: + specifier: ^3.8.1 + version: 3.8.3 + prettier-plugin-tailwindcss: + specifier: ^0.7.2 + version: 0.7.4(prettier@3.8.3) + prisma: + specifier: ^7.8.0 + version: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + tailwindcss: + specifier: ^4.2.1 + version: 4.3.0 + tsx: + specifier: ^4.21.0 + version: 4.21.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + apps/web: dependencies: '@prisma/adapter-pg': specifier: ^7.8.0 @@ -44,9 +83,6 @@ importers: pg: specifier: ^8.20.0 version: 8.20.0 - prisma: - specifier: ^7.8.0 - version: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3) radix-ui: specifier: ^1.4.3 version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -102,23 +138,14 @@ importers: postcss: specifier: ^8 version: 8.5.14 - prettier: - specifier: ^3.8.1 - version: 3.8.3 - prettier-plugin-tailwindcss: - specifier: ^0.7.2 - version: 0.7.4(prettier@3.8.3) tailwindcss: specifier: ^4.2.1 version: 4.3.0 - tsx: - specifier: ^4.21.0 - version: 4.21.0 typescript: specifier: ^5.9.3 version: 5.9.3 - cli: {} + packages/cli: {} packages: @@ -6835,7 +6862,7 @@ snapshots: '@next/eslint-plugin-next': 16.1.7 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) @@ -6858,7 +6885,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -6873,14 +6900,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color @@ -6895,7 +6922,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) hasown: 2.0.3 is-core-module: 2.16.2 is-glob: 4.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6bbcb3d..80a69b7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - - cli + - apps/* + - packages/* allowBuilds: "@prisma/engines": true "@scarf/scarf": true diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f25b04b..2735eae 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,6 @@ generator client { provider = "prisma-client" - output = "../generated/prisma" + output = "../apps/web/generated/prisma" moduleFormat = "esm" } diff --git a/scripts/seed-admin.ts b/scripts/seed-admin.ts index bbdeb75..a150b83 100644 --- a/scripts/seed-admin.ts +++ b/scripts/seed-admin.ts @@ -2,7 +2,8 @@ import "dotenv/config" import bcrypt from "bcryptjs" import { PrismaPg } from "@prisma/adapter-pg" -import { PrismaClient } from "../generated/prisma/client" +import pg from "pg" +import { PrismaClient } from "../apps/web/generated/prisma/client" const connectionString = process.env.DATABASE_URL @@ -10,17 +11,20 @@ if (!connectionString) { throw new Error("DATABASE_URL is not set") } +const pool = new pg.Pool({ connectionString, connectionTimeoutMillis: 5_000 }) const prisma = new PrismaClient({ - adapter: new PrismaPg({ connectionString }), + adapter: new PrismaPg(pool, { disposeExternalPool: true }), }) -const username = "admin" -const passwordHash = await bcrypt.hash("12345678", 12) +try { + const username = "admin" + const passwordHash = await bcrypt.hash("12345678", 12) -await prisma.user.upsert({ - where: { username }, - update: { passwordHash }, - create: { username, passwordHash }, -}) - -await prisma.$disconnect() + await prisma.user.upsert({ + where: { username }, + update: { passwordHash }, + create: { username, passwordHash }, + }) +} finally { + await prisma.$disconnect() +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..eb2322c --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 247f602..d390453 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,42 +1,7 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "baseUrl": ".", - "paths": { - "@/*": [ - "./*" - ] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts" - ], - "exclude": [ - "node_modules" + "files": [], + "references": [ + { "path": "apps/web" }, + { "path": "packages/cli" } ] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..67a3b5a --- /dev/null +++ b/vercel.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "nextjs", + "installCommand": "corepack pnpm install --frozen-lockfile", + "buildCommand": "corepack pnpm build:web", + "outputDirectory": "apps/web/.next" +}