diff --git a/.env.template b/.env.template index 7190077..20143a8 100644 --- a/.env.template +++ b/.env.template @@ -2,38 +2,10 @@ DATABASE_URL="" # Authentication -NEXTAUTH_URL="" -NEXTAUTH_SECRET="" -AUTH_TRUST_HOST="true" - -# GitHub App (required for GitHub login and repository import) -GITHUB_APP_ID="" -GITHUB_APP_PRIVATE_KEY="" -GITHUB_APP_WEBHOOK_SECRET="" -GITHUB_APP_CLIENT_ID="" -GITHUB_APP_CLIENT_SECRET="" -NEXT_PUBLIC_GITHUB_APP_NAME="" - -# Sealos OAuth (optional) -SEALOS_JWT_SECRET="" - -# Feature Flags -ENABLE_PASSWORD_AUTH="" -ENABLE_GITHUB_AUTH="true" -ENABLE_SEALOS_AUTH="" - -# Reconciliation -DATABASE_LOCK_DURATION_SECONDS="" -MAX_DATABASES_PER_RECONCILE="" -SANDBOX_LOCK_DURATION_SECONDS="" -MAX_SANDBOXES_PER_RECONCILE="" - -# Kubernetes -RUNTIME_IMAGE="" - -# AI Proxy -AIPROXY_ENDPOINT="" -ANTHROPIC_BASE_URL="" +BETTER_AUTH_URL="http://localhost:3000" +BETTER_AUTH_SECRET="" +GITHUB_CLIENT_ID="" +GITHUB_CLIENT_SECRET="" # Logging LOG_LEVEL="info" diff --git a/.github/workflows/build-runtime.yml b/.github/workflows/build-runtime.yml deleted file mode 100644 index 997c39f..0000000 --- a/.github/workflows/build-runtime.yml +++ /dev/null @@ -1,216 +0,0 @@ -name: Build Runtime Image - -on: - workflow_dispatch: - -permissions: - pull-requests: write - packages: write - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} - -jobs: - build-runtime-images: - name: Build Runtime Docker Images - permissions: - packages: write - runs-on: ubuntu-24.04 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Convert repository owner to lowercase - id: repo-owner - run: | - echo "lowercase=${GITHUB_REPOSITORY_OWNER@L}" >> $GITHUB_OUTPUT - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - if: ${{ github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' && env.DOCKERHUB_USERNAME != '' }} - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - if: ${{ github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' }} - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ steps.repo-owner.outputs.lowercase }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ghcr.io/${{ steps.repo-owner.outputs.lowercase }}/fullstack-web-runtime - ${{ env.DOCKERHUB_USERNAME && format('docker.io/{0}/fullstack-web-runtime', env.DOCKERHUB_USERNAME) || '' }} - tags: | - type=ref,event=branch - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=sha,prefix=sha- - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') || github.ref == format('refs/heads/{0}', 'master') }} - labels: | - org.opencontainers.image.title=FullStack Web Runtime - org.opencontainers.image.description=Full-stack web development runtime with Next.js, shadcn/ui, Claude Code CLI, and container tools - org.opencontainers.image.vendor=${{ steps.repo-owner.outputs.lowercase }} - - - name: Build and Push Docker Image - id: docker-build - uses: docker/build-push-action@v6 - with: - context: ./runtime - file: ./runtime/Dockerfile - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64 - tags: ${{ steps.meta.outputs.tags }} - # PR builds: load locally for validation, Push builds: push to registry - push: ${{ github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' }} - load: ${{ github.event_name == 'pull_request' }} - cache-from: type=gha,scope=runtime-amd64 - cache-to: type=gha,mode=max,scope=runtime-amd64 - - - name: Comment on PR - if: github.event_name == 'pull_request' && always() - uses: actions/github-script@v7 - continue-on-error: true - with: - script: | - const buildSuccess = '${{ steps.docker-build.outcome }}' === 'success'; - const emoji = buildSuccess ? '✅' : '❌'; - const status = buildSuccess ? 'Success' : 'Failed'; - - let body = `## ${emoji} FullStack Web Runtime Build ${status}\n\n`; - body += `### Build Details\n\n`; - body += `| Item | Value |\n`; - body += `|------|-------|\n`; - body += `| Build Status | ${buildSuccess ? '✅ Passed' : '❌ Failed'} |\n`; - body += `| Platforms | linux/amd64 (PR validation) |\n`; - body += `| Push to Registry | ⚠️ No (PR build only) |\n`; - body += `| Base Image | ubuntu:24.04 |\n`; - body += `| Node.js | 22.x LTS |\n`; - body += `| Components | Claude Code CLI, ttyd, Next.js, Prisma, PostgreSQL client, Buildah |\n\n`; - - if (buildSuccess) { - body += `### 📦 Runtime image will be published after merge\n\n`; - body += `**Note**: PR builds only verify the Docker build process. `; - body += `Images are pushed to registries only when merged to main.\n\n`; - body += `**Registries**:\n`; - body += `- GitHub Container Registry: \`ghcr.io/${{ steps.repo-owner.outputs.lowercase }}/fullstack-web-runtime\`\n`; - if ('${{ env.DOCKERHUB_USERNAME }}') { - body += `- Docker Hub: \`docker.io/${{ env.DOCKERHUB_USERNAME }}/fullstack-web-runtime\`\n`; - } - body += `\n**Included Tools**:\n`; - body += `- Node.js 22.x + npm, pnpm, yarn\n`; - body += `- Claude Code CLI (@anthropic-ai/claude-code)\n`; - body += `- Next.js with shadcn/ui components\n`; - body += `- PostgreSQL 16 client\n`; - body += `- Container tools (Buildah, Podman, Skopeo)\n`; - body += `- Development tools (Git, GitHub CLI, ripgrep, jq, etc.)\n`; - body += `- ttyd web terminal\n`; - } else { - body += `### ❌ Build Failed\n\n`; - body += `Please check the workflow logs for detailed error information.\n`; - } - - body += `\n---\n`; - body += `**Commit**: \`${{ github.sha }}\`\n`; - body += `**Triggered by**: @${{ github.actor }}\n`; - - try { - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const botComment = comments.find(comment => - comment.user.type === 'Bot' && - comment.body.includes('FullStack Web Runtime Build') - ); - - if (botComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - body: body - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: body - }); - } - } catch (error) { - console.log('Failed to post comment:', error.message); - console.log('This might be expected for PRs from forks'); - } - - - name: Generate build summary - if: ${{ github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' && always() }} - run: | - echo "## 🚀 Runtime Image Build & Push Report" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Build Status" >> $GITHUB_STEP_SUMMARY - if [ "${{ steps.docker-build.outcome }}" = "success" ]; then - echo "- ✅ Runtime build successful" >> $GITHUB_STEP_SUMMARY - echo "- ✅ Platform: \`linux/amd64\`" >> $GITHUB_STEP_SUMMARY - echo "- ✅ Pushed to GitHub Container Registry: \`ghcr.io/${{ steps.repo-owner.outputs.lowercase }}/fullstack-web-runtime\`" >> $GITHUB_STEP_SUMMARY - if [ -n "${{ env.DOCKERHUB_USERNAME }}" ]; then - echo "- ✅ Pushed to Docker Hub: \`docker.io/${{ env.DOCKERHUB_USERNAME }}/fullstack-web-runtime\`" >> $GITHUB_STEP_SUMMARY - fi - else - echo "- ❌ Build or push failed" >> $GITHUB_STEP_SUMMARY - fi - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Runtime Image Details" >> $GITHUB_STEP_SUMMARY - echo "- **Base**: Ubuntu 24.04" >> $GITHUB_STEP_SUMMARY - echo "- **Node.js**: 22.x LTS" >> $GITHUB_STEP_SUMMARY - echo "- **PostgreSQL Client**: 16" >> $GITHUB_STEP_SUMMARY - echo "- **Claude Code CLI**: @anthropic-ai/claude-code" >> $GITHUB_STEP_SUMMARY - echo "- **Next.js**: Latest with shadcn/ui components" >> $GITHUB_STEP_SUMMARY - echo "- **Container Tools**: Buildah, Podman, Skopeo" >> $GITHUB_STEP_SUMMARY - echo "- **Terminal**: ttyd web-based terminal" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Build Information" >> $GITHUB_STEP_SUMMARY - echo "- **Commit SHA**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Triggered by**: @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY - echo "- **Event**: \`${{ github.event_name }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Build time**: $(date '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Image Tags" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Usage Example" >> $GITHUB_STEP_SUMMARY - echo '```bash' >> $GITHUB_STEP_SUMMARY - echo "# Pull the latest image" >> $GITHUB_STEP_SUMMARY - echo "docker pull ghcr.io/${{ steps.repo-owner.outputs.lowercase }}/fullstack-web-runtime:latest" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "# Run with environment variables" >> $GITHUB_STEP_SUMMARY - echo "docker run -d -p 7681:7681 -p 3000:3000 \\" >> $GITHUB_STEP_SUMMARY - echo " -e ANTHROPIC_AUTH_TOKEN=your_token \\" >> $GITHUB_STEP_SUMMARY - echo " -e PROJECT_NAME=my-project \\" >> $GITHUB_STEP_SUMMARY - echo " ghcr.io/${{ steps.repo-owner.outputs.lowercase }}/fullstack-web-runtime:latest" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c98dcd..51909d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,13 +16,32 @@ env: NODE_VERSION: "22.12.0" PNPM_VERSION: "10.20.0" NEXT_TELEMETRY_DISABLED: 1 - SKIP_ENV_VALIDATION: 1 + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/fulling_test + BETTER_AUTH_URL: http://127.0.0.1:3000 + BETTER_AUTH_SECRET: ci-better-auth-secret-at-least-32-characters + GITHUB_CLIENT_ID: test-github-client-id + GITHUB_CLIENT_SECRET: test-github-client-secret jobs: lint-test-build: name: Lint, Test, Build runs-on: ubuntu-latest + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_DB: fulling_test + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres -d fulling_test" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: - name: Checkout code uses: actions/checkout@v4 @@ -41,11 +60,20 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Deploy database baseline + run: pnpm prisma:migrate + - name: Run linter run: pnpm lint - name: Run tests run: pnpm test + - name: Install Playwright Chromium + run: pnpm exec playwright install --with-deps chromium + + - name: Run browser tests + run: pnpm test:e2e + - name: Build project run: pnpm build diff --git a/.gitignore b/.gitignore index 51b965d..5517136 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ # testing /coverage +/playwright-report/ +/test-results/ +/e2e/.auth/ # next.js /.next/ @@ -42,8 +45,12 @@ yarn-error.log* *.pem *.crt *.pfx -kubeconfig -*kubeconfig* +/kubeconfig +/kubeconfig.yaml +/kubeconfig.yml +*.kubeconfig +*.kubeconfig.yaml +*.kubeconfig.yml credentials.json secrets.yaml secrets.yml diff --git a/AGENTS.md b/AGENTS.md index fa38a85..1ecbd00 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,8 +12,8 @@ Read [docs/architecture.md](./docs/architecture.md) before product or architectu - Next.js 16 (App Router) + React 19 + TypeScript - Tailwind CSS v4 + Shadcn/UI -- Node.js 22 + Prisma + NextAuth v5 -- Kubernetes + PostgreSQL (KubeBlocks) +- Node.js 22 + Prisma + Better Auth +- Kubernetes + PostgreSQL ## Code Conventions @@ -25,11 +25,13 @@ Read [docs/architecture.md](./docs/architecture.md) before product or architectu ## Current Implementation Context -- Existing code still contains v2 project/resource abstractions. Do not treat them as v3 product architecture. -- **Asynchronous reconciliation** exists in current code: API → Database → Reconciliation Job → Event → K8s Operation -- **Always use user-specific K8s service**: `const k8sService = await getK8sServiceForUser(userId)` -- **Optimistic locking** in Repository layer -- **Non-blocking APIs**: endpoints only update DB, return immediately +- GitHub is the only authentication provider. +- Better Auth stores users, provider accounts, and sessions in PostgreSQL. +- Each user may store one plaintext kubeconfig; browser APIs never return it. +- **Always use the user-specific K8s service**: + `const k8sService = await getK8sServiceForUser(userId)`. +- The current user-level kubeconfig is a foundation boundary, not the final v3 + Workspace Runtime ownership model. ## UI Direction @@ -43,9 +45,9 @@ Avoid generic AI copywriting cliches such as "Elevate", "Seamless", and ## Key Files - [docs/architecture.md](./docs/architecture.md) — v3 system architecture and product model -- `lib/k8s/k8s-service-helper.ts` — User-specific K8s service -- `lib/events/` + `lib/jobs/` — Reconciliation core -- `instrumentation.ts` — Application startup +- `lib/auth/` — Better Auth and application session boundary +- `lib/kubeconfig/` — User credential persistence +- `lib/k8s/` — Validation and user-specific Kubernetes service ## Image Tagging Policy @@ -90,9 +92,10 @@ Do not use ambiguous moving tags such as `main`, `stable`, `prod`, `release`, ## Development Commands ```bash -pnpm dev # Start dev server -pnpm build # Build for production -pnpm lint # Run ESLint -npx prisma generate # Generate Prisma client -npx prisma db push # Push schema to database +corepack pnpm dev # Start dev server +corepack pnpm build # Build for production +corepack pnpm lint # Run ESLint +corepack pnpm test # Run Vitest +corepack pnpm test:e2e # Run Playwright +corepack pnpm prisma:migrate # Deploy the baseline migration ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8adffc5..5feba05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,42 +1,31 @@ # Contributing to Fulling -Thank you for your interest in Fulling. This document covers the essentials for contributing effectively. - -## Project Direction - -Fulling v3.0 is building **dedicated AI workspaces** — persistent, personalizable environments with skills, files, memory, scripts, and runtime. Before contributing, read [README.md](./README.md) for the product vision and [design/DESIGN.md](./design/DESIGN.md) for the UI design system. +Fulling v3 is building dedicated AI workspaces. Read +[docs/architecture.md](./docs/architecture.md) before product or architecture +work. ## Development Setup -Prerequisites: Node.js 22.12.0+, pnpm 10.20.0+, PostgreSQL, Kubernetes with KubeBlocks. +Requirements: Node.js 22.12+, pnpm 10.20.0, PostgreSQL, and GitHub OAuth +credentials. ```bash -git clone https://github.com/FullAgent/fulling.git -cd fulling -pnpm install +corepack pnpm install cp .env.template .env.local -# Edit .env.local with your settings -npx prisma generate -npx prisma db push -pnpm dev +corepack pnpm prisma:migrate +corepack pnpm dev ``` -Generated repository documentation lives in [.qoder/repowiki](./.qoder/repowiki). -Regenerate it when code structure changes materially. +The baseline migration targets a new or explicitly reset database. There is no +v2 data migration. ## Before Submitting -- [ ] `pnpm lint` passes -- [ ] `pnpm build` succeeds -- [ ] UI changes follow [design/DESIGN.md](./design/DESIGN.md) -- [ ] Commits use conventional format: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:` - -## Reporting Issues - -- Search existing issues first -- One issue per bug or feature request -- Include: reproduction steps, expected vs actual behavior, environment info - -## Questions? +- `corepack pnpm lint` +- `corepack pnpm test` +- `corepack pnpm test:e2e` +- `corepack pnpm build` +- verify that no browser response or log includes kubeconfig content -Open a [GitHub Discussion](https://github.com/FullAgent/fulling/discussions) or ask in an existing issue. +Commits use conventional prefixes such as `feat:`, `fix:`, `docs:`, `refactor:`, +and `chore:`. diff --git a/Dockerfile b/Dockerfile index 9fa064f..544e755 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Install dependencies only when needed -FROM node:current-alpine AS deps +FROM node:22-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat openssl && npm install -g pnpm +RUN apk add --no-cache libc6-compat openssl && corepack enable && corepack prepare pnpm@10.20.0 --activate WORKDIR /app # Copy package files and prisma schema @@ -18,7 +18,7 @@ RUN \ fi # Rebuild the source code only when needed -FROM node:current-alpine AS builder +FROM node:22-alpine AS builder RUN apk add --no-cache openssl WORKDIR /app COPY --from=deps /app/node_modules ./node_modules @@ -29,16 +29,15 @@ COPY . . # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. ENV NEXT_TELEMETRY_DISABLED=1 -ENV NEXT_PUBLIC_MOCK_USER='' ENV SKIP_ENV_VALIDATION=1 -# Install pnpm and generate Prisma client before build -RUN npm install -g pnpm && \ +# Generate Prisma client before build +RUN corepack enable && corepack prepare pnpm@10.20.0 --activate && \ pnpm prisma generate && \ pnpm run build # Production image, copy all the files and run next -FROM node:current-alpine AS runner +FROM node:22-alpine AS runner WORKDIR /app ENV NODE_ENV=production @@ -49,7 +48,6 @@ RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs # Install runtime dependencies including OpenSSL for Prisma -RUN sed -i 's/https/http/' /etc/apk/repositories RUN apk add --no-cache \ curl \ ca-certificates \ diff --git a/README.md b/README.md index 675f3e4..747ae99 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,84 @@ # Fulling -
Package AI workspaces once. Share them with people who just need the AI to work.
+## Current Foundation -
-
-
-
-
-
- Overview • - Workspace model • - How it works • - Project status • - Local development -
-{errorDetails.message}
-- If this problem persists, please contact support or check your authentication - configuration. -
-{error}
: null} +{userName}
{userEmail}
- Manage and monitor your full stack project workspaces. -
-- {description} -
- - {/* Card Footer */} -{description}
-- A PostgreSQL cluster will be created with 1Gi storage, 100m CPU, and 128Mi memory. -
-
-
{description}
-Database is being created...
-- Connection details will appear once the database is ready -
-- ttydUrl: {sandbox?.ttydUrl || 'Not available'} -
-- accessToken: {ttydAccessToken ? 'Configured' : 'Not configured'} -
-- Sandbox Status: {sandbox?.status || 'No sandbox'} -
-- Make sure the sandbox is RUNNING and TTYD_ACCESS_TOKEN is set in environments. -
-- {hasRepo - ? 'Your project is currently active and synced with a remote GitHub repository. You can push your latest changes below.' - : 'Initialize a new repository to start tracking changes. This will create a private repository in your GitHub account and push the initial code.'} -
-Manage your global GitHub connection and personal access tokens.
-- Install the GitHub App to grant access to your repositories. -
-- Connecting to GitHub allows you to import repositories and keep project source metadata available in Fulling. -
- - {installation ? ( -- Manage your integrations and connected services. -
-Settings
++ Fulling validates this credential against the selected Kubernetes cluster before storing + it as plaintext. Saved content is never displayed again. +
++ {isLoading + ? 'Loading status...' + : status?.configured + ? `Configured. Last updated ${formatUpdatedAt(status.updatedAt)}.` + : 'No kubeconfig is configured.'} +
++ Paste the complete replacement. Existing content is intentionally not prefilled. +
+ ++ Your account and session remain active after deletion. +
+- Skills here define global desired state for the user. Enabling fans out - `INSTALL_SKILL` tasks to existing projects and future projects inherit the skill. - Uninstalling removes the global desired state and fans out `UNINSTALL_SKILL` work - without auto-starting stopped sandboxes. -
-
- {skill.installCommand}
-
- Workspace
{user.name}
{user.email}
Loading status...
: null} + {error ?{error}
Last updated
{formatUpdatedAt(status.updatedAt)}
- Build, run, and share AI workspaces as products.
-
- Versioned. Reliable. Governed.
-
- {item.title} -
-- {item.detail} -
-Dedicated AI workspaces
+Connect the Kubernetes credentials that will power your workspace runtime.
+- An unexpected error occurred. Please try again or return to the home page. -
-Completing GitHub App installation...
- > - )} - {status === 'success' && ( - <> -GitHub App installed successfully!
-This window will close automatically...
- > - )} - {status === 'error' && ( - <> -Installation failed
-{errorMessage}
- > - )} -- The page you're looking for doesn't exist or has been moved. -
-- Check the URL for typos, or use the navigation above to find what you need. -
+404
++ The page does not exist or has moved. +
+{description}
} -{template.description}
- )} -- Environment variables can only be updated when the project is running -
- )} -AI-Powered Full-Stack Development Platform
-- Create, develop, and deploy production-ready web applications using natural language. - Powered by Claude Code in isolated sandbox environments. -
-