From e77075ebda9188178bf3faa3cb6460d5d405a898 Mon Sep 17 00:00:00 2001 From: Seva Date: Fri, 31 Jul 2026 00:56:06 +0000 Subject: [PATCH] feat: ship managed collaborative platform v4 --- .env.example | 16 + app/api/platform/capabilities/route.ts | 16 + app/backend/page.tsx | 9 + app/enterprise/page.tsx | 9 + app/integrations/page.tsx | 9 + app/layout.tsx | 8 +- app/organizations/page.tsx | 9 + app/page.tsx | 15 +- app/platform/page.tsx | 13 + app/projects/page.tsx | 9 + app/styles/drops-studio.responsive.css | 1 + app/styles/drops-studio.shell.css | 2 +- app/styles/platform-tailwind.css | 5 + app/templates/page.tsx | 9 + components/drops-studio.tsx | 33 +- components/platform/integration-catalog.tsx | 82 +++ components/platform/organization-console.tsx | 137 +++++ .../platform/platform-capability-console.tsx | 181 +++++++ components/platform/platform-overview.tsx | 58 +++ components/platform/platform-shell.tsx | 82 +++ components/platform/platform-ui.tsx | 58 +++ components/platform/project-library.tsx | 58 +++ components/platform/template-catalog.tsx | 95 ++++ docs/agent/skills/AUDIT_AND_COMPLIANCE.md | 7 + docs/agent/skills/COLLABORATION.md | 7 + docs/agent/skills/DATA_MODELING.md | 7 + docs/agent/skills/ENTERPRISE_RBAC.md | 7 + docs/agent/skills/ENTERPRISE_SSO.md | 7 + docs/agent/skills/JOBS_AND_CRON.md | 7 + docs/agent/skills/MANAGED_AUTH.md | 7 + docs/agent/skills/MANAGED_BACKEND.md | 8 + docs/agent/skills/OBJECT_STORAGE.md | 7 + docs/agent/skills/REALTIME_DATA.md | 7 + docs/agent/skills/SERVER_FUNCTIONS.md | 7 + docs/agent/skills/WEBHOOKS.md | 7 + docs/platform/AUDIT_RETENTION_EXPORT.md | 7 + docs/platform/BACKUP_AND_RECOVERY.md | 7 + docs/platform/COLLABORATION_ARCHITECTURE.md | 7 + docs/platform/ENTERPRISE_IDENTITY.md | 7 + docs/platform/ENTERPRISE_POLICIES.md | 7 + docs/platform/FUNCTIONS_JOBS_CRON.md | 7 + docs/platform/MANAGED_AUTH.md | 7 + docs/platform/MANAGED_BACKEND.md | 15 + .../MANAGED_PLATFORM_COST_CONTROLS.md | 11 + docs/platform/MANAGED_PLATFORM_OPERATIONS.md | 10 + docs/platform/MANAGED_PLATFORM_SECURITY.md | 12 + docs/platform/ORGANIZATIONS_AND_RBAC.md | 7 + docs/platform/REALTIME_AND_WEBHOOKS.md | 7 + e2e/contracts/managed-platform-v4.spec.ts | 81 +++ lib/agent/evals/dashboard-types.ts | 2 +- lib/agent/runtime/intelligent-builder.ts | 59 ++- lib/agent/skills/registry.ts | 180 +++++++ lib/agent/skills/selector.ts | 12 + lib/agent/skills/types.ts | 12 + lib/enterprise-platform/audit.ts | 126 +++++ lib/enterprise-platform/branches.ts | 266 ++++++++++ lib/enterprise-platform/collaboration.ts | 361 ++++++++++++++ lib/enterprise-platform/credentials.ts | 196 ++++++++ lib/enterprise-platform/errors.ts | 54 ++ lib/enterprise-platform/feature-states.ts | 24 + lib/enterprise-platform/identity.ts | 267 ++++++++++ lib/enterprise-platform/index.ts | 11 + lib/enterprise-platform/lifecycle.ts | 317 ++++++++++++ lib/enterprise-platform/organizations.ts | 468 ++++++++++++++++++ lib/enterprise-platform/policies.ts | 195 ++++++++ lib/enterprise-platform/types.ts | 64 +++ lib/enterprise-platform/utils.ts | 116 +++++ lib/managed-platform/auth.ts | 184 +++++++ lib/managed-platform/backups.ts | 123 +++++ lib/managed-platform/contracts.ts | 190 +++++++ lib/managed-platform/data.ts | 363 ++++++++++++++ lib/managed-platform/index.ts | 6 + lib/managed-platform/logs.ts | 39 ++ lib/managed-platform/platform.ts | 124 +++++ lib/managed-platform/providers.ts | 48 ++ lib/managed-platform/runtime-services.ts | 396 +++++++++++++++ lib/managed-platform/secrets.ts | 117 +++++ lib/managed-platform/security.ts | 143 ++++++ lib/managed-platform/storage.ts | 82 +++ lib/platform-capabilities.ts | 189 +++++++ lib/project-template-managed.ts | 343 +++++++++++++ lib/project-template-materializer.ts | 7 +- lib/project-template-ui.ts | 15 +- .../agent-evals/v3/compact-core-metrics.json | 4 +- outputs/agent-evals/v3/manifest.json | 2 +- tests/agent-intelligence-runtime.test.mjs | 1 + tests/agent-prompt-skills-v3.test.mjs | 39 ++ ...enterprise-platform-collaboration.test.mjs | 102 ++++ ...terprise-platform-identity-policy.test.mjs | 155 ++++++ tests/enterprise-platform-lifecycle.test.mjs | 134 +++++ ...enterprise-platform-organizations.test.mjs | 180 +++++++ tests/managed-platform-backups.test.mjs | 60 +++ tests/managed-platform-data.test.mjs | 215 ++++++++ tests/managed-platform-e2e-v4.test.mjs | 128 +++++ tests/managed-platform-services.test.mjs | 243 +++++++++ tests/platform-capabilities.test.mjs | 42 ++ tests/platform-public-surfaces.test.mjs | 84 ++++ tests/project-template-materializer.test.mjs | 65 +++ tests/rendered-html.test.mjs | 2 +- 99 files changed, 7704 insertions(+), 42 deletions(-) create mode 100644 app/api/platform/capabilities/route.ts create mode 100644 app/backend/page.tsx create mode 100644 app/enterprise/page.tsx create mode 100644 app/integrations/page.tsx create mode 100644 app/organizations/page.tsx create mode 100644 app/platform/page.tsx create mode 100644 app/projects/page.tsx create mode 100644 app/styles/platform-tailwind.css create mode 100644 app/templates/page.tsx create mode 100644 components/platform/integration-catalog.tsx create mode 100644 components/platform/organization-console.tsx create mode 100644 components/platform/platform-capability-console.tsx create mode 100644 components/platform/platform-overview.tsx create mode 100644 components/platform/platform-shell.tsx create mode 100644 components/platform/platform-ui.tsx create mode 100644 components/platform/project-library.tsx create mode 100644 components/platform/template-catalog.tsx create mode 100644 docs/agent/skills/AUDIT_AND_COMPLIANCE.md create mode 100644 docs/agent/skills/COLLABORATION.md create mode 100644 docs/agent/skills/DATA_MODELING.md create mode 100644 docs/agent/skills/ENTERPRISE_RBAC.md create mode 100644 docs/agent/skills/ENTERPRISE_SSO.md create mode 100644 docs/agent/skills/JOBS_AND_CRON.md create mode 100644 docs/agent/skills/MANAGED_AUTH.md create mode 100644 docs/agent/skills/MANAGED_BACKEND.md create mode 100644 docs/agent/skills/OBJECT_STORAGE.md create mode 100644 docs/agent/skills/REALTIME_DATA.md create mode 100644 docs/agent/skills/SERVER_FUNCTIONS.md create mode 100644 docs/agent/skills/WEBHOOKS.md create mode 100644 docs/platform/AUDIT_RETENTION_EXPORT.md create mode 100644 docs/platform/BACKUP_AND_RECOVERY.md create mode 100644 docs/platform/COLLABORATION_ARCHITECTURE.md create mode 100644 docs/platform/ENTERPRISE_IDENTITY.md create mode 100644 docs/platform/ENTERPRISE_POLICIES.md create mode 100644 docs/platform/FUNCTIONS_JOBS_CRON.md create mode 100644 docs/platform/MANAGED_AUTH.md create mode 100644 docs/platform/MANAGED_BACKEND.md create mode 100644 docs/platform/MANAGED_PLATFORM_COST_CONTROLS.md create mode 100644 docs/platform/MANAGED_PLATFORM_OPERATIONS.md create mode 100644 docs/platform/MANAGED_PLATFORM_SECURITY.md create mode 100644 docs/platform/ORGANIZATIONS_AND_RBAC.md create mode 100644 docs/platform/REALTIME_AND_WEBHOOKS.md create mode 100644 e2e/contracts/managed-platform-v4.spec.ts create mode 100644 lib/enterprise-platform/audit.ts create mode 100644 lib/enterprise-platform/branches.ts create mode 100644 lib/enterprise-platform/collaboration.ts create mode 100644 lib/enterprise-platform/credentials.ts create mode 100644 lib/enterprise-platform/errors.ts create mode 100644 lib/enterprise-platform/feature-states.ts create mode 100644 lib/enterprise-platform/identity.ts create mode 100644 lib/enterprise-platform/index.ts create mode 100644 lib/enterprise-platform/lifecycle.ts create mode 100644 lib/enterprise-platform/organizations.ts create mode 100644 lib/enterprise-platform/policies.ts create mode 100644 lib/enterprise-platform/types.ts create mode 100644 lib/enterprise-platform/utils.ts create mode 100644 lib/managed-platform/auth.ts create mode 100644 lib/managed-platform/backups.ts create mode 100644 lib/managed-platform/contracts.ts create mode 100644 lib/managed-platform/data.ts create mode 100644 lib/managed-platform/index.ts create mode 100644 lib/managed-platform/logs.ts create mode 100644 lib/managed-platform/platform.ts create mode 100644 lib/managed-platform/providers.ts create mode 100644 lib/managed-platform/runtime-services.ts create mode 100644 lib/managed-platform/secrets.ts create mode 100644 lib/managed-platform/security.ts create mode 100644 lib/managed-platform/storage.ts create mode 100644 lib/platform-capabilities.ts create mode 100644 lib/project-template-managed.ts create mode 100644 tests/enterprise-platform-collaboration.test.mjs create mode 100644 tests/enterprise-platform-identity-policy.test.mjs create mode 100644 tests/enterprise-platform-lifecycle.test.mjs create mode 100644 tests/enterprise-platform-organizations.test.mjs create mode 100644 tests/managed-platform-backups.test.mjs create mode 100644 tests/managed-platform-data.test.mjs create mode 100644 tests/managed-platform-e2e-v4.test.mjs create mode 100644 tests/managed-platform-services.test.mjs create mode 100644 tests/platform-capabilities.test.mjs create mode 100644 tests/platform-public-surfaces.test.mjs diff --git a/.env.example b/.env.example index 0068853..ec333e6 100644 --- a/.env.example +++ b/.env.example @@ -101,3 +101,19 @@ STRIPE_SECRET_KEY= STRIPE_WEBHOOK_SECRET= STRIPE_PRO_PRICE_ID= DROPS_TEAM_INVITE_SECRET= + +# Managed Platform V4. These values select external adapters; setting a name never +# marks the adapter healthy by itself. Production UI waits for provider evidence. +# DROPS_MANAGED_DATA_PROVIDER accepts only "d1" or "postgres" in adapter wiring. +DROPS_MANAGED_DATA_PROVIDER= +DATABASE_URL= +DROPS_COLLABORATION_TRANSPORT_URL= + +# Generic enterprise OIDC. Values stay server-only and external login remains +# disabled until discovery, callback, nonce, PKCE, domain and group checks pass. +DROPS_ENTERPRISE_OIDC_ISSUER= +DROPS_ENTERPRISE_OIDC_CLIENT_ID= +DROPS_ENTERPRISE_OIDC_CLIENT_SECRET= + +# Separate 32+ byte material for a future durable append-only audit adapter. +DROPS_ENTERPRISE_AUDIT_SIGNING_KEY= diff --git a/app/api/platform/capabilities/route.ts b/app/api/platform/capabilities/route.ts new file mode 100644 index 0000000..0b1fb7f --- /dev/null +++ b/app/api/platform/capabilities/route.ts @@ -0,0 +1,16 @@ +import { NextResponse } from "next/server.js"; + +import { platformCapabilitySnapshot } from "@/lib/platform-capabilities"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +export async function GET() { + return NextResponse.json(platformCapabilitySnapshot(), { + status: 200, + headers: { + "cache-control": "private, no-store, max-age=0", + vary: "Cookie", + }, + }); +} diff --git a/app/backend/page.tsx b/app/backend/page.tsx new file mode 100644 index 0000000..253c060 --- /dev/null +++ b/app/backend/page.tsx @@ -0,0 +1,9 @@ +import { Database, ShieldCheck } from "lucide-react"; + +import { PlatformCapabilityConsole } from "@/components/platform/platform-capability-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function BackendPage() { + return
Data-plane readiness

Live server capability receipt

Reference core verified
} />
; +} diff --git a/app/enterprise/page.tsx b/app/enterprise/page.tsx new file mode 100644 index 0000000..ad2fc2f --- /dev/null +++ b/app/enterprise/page.tsx @@ -0,0 +1,9 @@ +import { ShieldCheck, UsersRound } from "lucide-react"; + +import { PlatformCapabilityConsole } from "@/components/platform/platform-capability-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function EnterprisePage() { + return
Control-plane evidence

Tenant-safe reference runtime

Core verified locally
} />
; +} diff --git a/app/integrations/page.tsx b/app/integrations/page.tsx new file mode 100644 index 0000000..8df4fc2 --- /dev/null +++ b/app/integrations/page.tsx @@ -0,0 +1,9 @@ +import { KeyRound, ShieldCheck } from "lucide-react"; + +import { IntegrationCatalog } from "@/components/platform/integration-catalog"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function IntegrationsPage() { + return
No credential values rendered

Only current-tab markers are inspected

Evidence required
} />
; +} diff --git a/app/layout.tsx b/app/layout.tsx index d53e25d..dd91cb0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -26,12 +26,12 @@ const productionUrl = /^https?:\/\//i.test(configuredProductionUrl) export const metadata: Metadata = { metadataBase: new URL(productionUrl), - title: "Drops Studio — Build crypto products in minutes", + title: "Drops Studio — Build crypto apps 10x faster with AI", description: - "Build useful crypto apps with DropsTab intelligence, guided Drops Bot setup and the AI model you choose.", + "Plan, build, test and publish editable crypto applications with DropsTab intelligence, Drops Bot automation and the AI model you choose.", openGraph: { title: "Drops Studio", - description: "Turn a crypto idea into a live project in five minutes.", + description: "Build editable crypto applications with AI, DropsTab intelligence and Drops Bot automation.", type: "website", images: [ { @@ -45,7 +45,7 @@ export const metadata: Metadata = { twitter: { card: "summary_large_image", title: "Drops Studio", - description: "Turn a crypto idea into a live project in five minutes.", + description: "Build editable crypto applications with AI, DropsTab intelligence and Drops Bot automation.", images: ["/og.png"], }, }; diff --git a/app/organizations/page.tsx b/app/organizations/page.tsx new file mode 100644 index 0000000..e1e506b --- /dev/null +++ b/app/organizations/page.tsx @@ -0,0 +1,9 @@ +import { Building2, ShieldCheck } from "lucide-react"; + +import { OrganizationConsole } from "@/components/platform/organization-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function OrganizationsPage() { + return
Tenant-aware control plane

Account and storage status are read live

Account dependent
} />
; +} diff --git a/app/page.tsx b/app/page.tsx index 8e87c83..af6fa8f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,21 +5,22 @@ function LandingHero() { return (
- BUILD IN 5 MINUTES + DROPS AI CRYPTO BUILDER

- Turn a crypto idea{" "} + Build crypto apps{" "}
- into a live project. + 10x faster with AI.

- Start from 12 extensible working foundations or describe any crypto - product. + Describe a product or start from 12 extensible working foundations + for category-native crypto apps. Review the plan, then build an editable + multi-file application.

- Drops Studio assembles the data, triggers, AI brain and output around - DropsTab + Drops Bot. + DropsTab supplies market intelligence. Drops Bot supplies monitoring, + alerts and approved Telegram delivery. You own the source.

diff --git a/app/platform/page.tsx b/app/platform/page.tsx new file mode 100644 index 0000000..3e59095 --- /dev/null +++ b/app/platform/page.tsx @@ -0,0 +1,13 @@ +import { Boxes, ShieldCheck } from "lucide-react"; + +import { PlatformOverview } from "@/components/platform/platform-overview"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { platformCapabilitySnapshot } from "@/lib/platform-capabilities"; + +export const dynamic = "force-dynamic"; + +export default function PlatformPage() { + const snapshot = platformCapabilitySnapshot(); + return
Capability-aware UI

Working, local, and setup states stay distinct

Truthful states
} />
; +} diff --git a/app/projects/page.tsx b/app/projects/page.tsx new file mode 100644 index 0000000..e974c51 --- /dev/null +++ b/app/projects/page.tsx @@ -0,0 +1,9 @@ +import { FolderKanban, HardDrive } from "lucide-react"; + +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { ProjectLibrary } from "@/components/platform/project-library"; + +export default function ProjectsPage() { + return
Browser-first library

Cloud sync remains account-dependent

Local source of truth
} />
; +} diff --git a/app/styles/drops-studio.responsive.css b/app/styles/drops-studio.responsive.css index 41889c6..e664923 100644 --- a/app/styles/drops-studio.responsive.css +++ b/app/styles/drops-studio.responsive.css @@ -13,6 +13,7 @@ .studio-header nav.open { display: flex; align-items: stretch; } .studio-header nav button, .studio-header nav a { border-radius: 8px; justify-content: space-between; padding: 11px; width: 100%; } .studio-header nav button:hover, .studio-header nav a:hover { background: #f3f6fb; } + .studio-header nav .mobile-nav-connections { display: flex; } .header-actions { min-width: 0; } .mobile-menu { display: flex; } .api-vault-button { display: none; } diff --git a/app/styles/drops-studio.shell.css b/app/styles/drops-studio.shell.css index d3df3dc..737c642 100644 --- a/app/styles/drops-studio.shell.css +++ b/app/styles/drops-studio.shell.css @@ -27,8 +27,8 @@ .studio-header nav button, .studio-header nav a { align-items: center; background: none; border: 0; color: #36435d; cursor: pointer; display: flex; font-size: 14px; font-weight: 560; gap: 6px; padding: 10px 0; text-decoration: none; } .studio-header nav button:hover, .studio-header nav a:hover { color: var(--blue); } .studio-header nav button span { align-items: center; background: #eaf0fb; border-radius: 99px; display: inline-flex; font-size: 12px; height: 18px; justify-content: center; min-width: 18px; } +.studio-header nav .mobile-nav-connections { display: none; } .header-actions { align-items: center; display: flex; justify-content: flex-end; min-width: 218px; } .api-vault-button { align-items: center; background: #fff; border: 1px solid #dbe3ef; border-radius: 11px; cursor: pointer; display: flex; font-size: 13px; font-weight: 650; gap: 7px; padding: 10px 14px; box-shadow: 0 5px 16px rgba(40, 60, 100, .05); } .api-vault-button:hover { border-color: #adc4ff; color: var(--blue); } .mobile-menu { background: transparent; border: 0; cursor: pointer; display: none; padding: 7px; } - diff --git a/app/styles/platform-tailwind.css b/app/styles/platform-tailwind.css new file mode 100644 index 0000000..ec105e3 --- /dev/null +++ b/app/styles/platform-tailwind.css @@ -0,0 +1,5 @@ +/* Public dashboard routes are isolated from the legacy landing cascade. Keep + these utilities unlayered so explicit component classes can override the + older unlayered h1-h6 reset without changing the approved builder shell. */ +@import "tailwindcss/theme.css"; +@import "tailwindcss/utilities.css"; diff --git a/app/templates/page.tsx b/app/templates/page.tsx new file mode 100644 index 0000000..45bc2fd --- /dev/null +++ b/app/templates/page.tsx @@ -0,0 +1,9 @@ +import { Layers3, ShieldCheck } from "lucide-react"; + +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { TemplateCatalog } from "@/components/platform/template-catalog"; + +export default function TemplatesPage() { + return
Current recipe catalog

Imported directly from lib/presets

12 registered
} />
; +} diff --git a/components/drops-studio.tsx b/components/drops-studio.tsx index 870e4ab..bb3fb00 100644 --- a/components/drops-studio.tsx +++ b/components/drops-studio.tsx @@ -347,10 +347,12 @@ function Brand() { function useNearViewport() { const [ready, setReady] = useState(false); - const elementRef = useRef(null); + const [element, setElement] = useState(null); + const elementRef = useCallback((next: HTMLElement | null) => { + setElement(next); + }, []); useEffect(() => { - const element = elementRef.current; if (!element) return; const observer = new IntersectionObserver( ([entry]) => { @@ -362,7 +364,7 @@ function useNearViewport() { ); observer.observe(element); return () => observer.disconnect(); - }, []); + }, [element]); return { ready, elementRef }; } @@ -476,6 +478,13 @@ export function DropsStudio({ hero }: { hero: ReactNode }) { useEffect(() => { const timer = window.setTimeout(async () => { + const params = new URLSearchParams(window.location.search); + const presetParam = params.get("preset"); + const requestedCatalogPreset = presets.find( + (preset) => preset.id === presetParam, + ); + if (requestedCatalogPreset) setSelectedId(requestedCatalogPreset.id); + let savedProjects = readProjectsFromStore(); try { @@ -612,7 +621,6 @@ export function DropsStudio({ hero }: { hero: ReactNode }) { } catch { /* The local compiler remains available when access status is offline. */ } - const params = new URLSearchParams(window.location.search); const handoff = parseStudioConnectionHandoff(window.location.search); if (handoff.connections) { const requestedProvider = providerList.find( @@ -1701,17 +1709,7 @@ export function DropsStudio({ hero }: { hero: ReactNode }) {