Wire operator Integrations page + capability-gating framework

Completes the arcadia-admin operator surface for the integration registry and
the capability/route-guard framework it depends on.

- Integration registry: route + Data-group nav entry + `platform.integrations`
  capability; the in-app client now delegates to the shared
  `@crema/integration-registry-client` lib (vite alias + tsconfig); the
  operator Integrations page (committed earlier) is now reachable.
- Capability gating: capabilities map + route-guard + jwt helpers + the
  apps/plan/entitlements routes and supporting tenants/session changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-06-09 23:09:24 +10:00
parent 06490865d3
commit 4b817b85ff
15 changed files with 1176 additions and 341 deletions

View File

@@ -62,6 +62,9 @@ const searchUiSrc = fileURLToPath(
const arcadiaClientSrc = fileURLToPath(
new URL("../lib-arcadia-client/src", import.meta.url),
)
const integrationRegistryClientSrc = fileURLToPath(
new URL("../lib-integration-registry-client/src", import.meta.url),
)
const arcadiaAuthUiSrc = fileURLToPath(
new URL("../lib-arcadia-auth-ui/src", import.meta.url),
)
@@ -86,6 +89,24 @@ const chartUiSrc = fileURLToPath(
const statusUiSrc = fileURLToPath(
new URL("../lib-status-ui/src", import.meta.url),
)
const actionBusSrc = fileURLToPath(
new URL("../lib-action-bus/src", import.meta.url),
)
const agentUiSrc = fileURLToPath(
new URL("../lib-agent-ui/src", import.meta.url),
)
const aifirstUiSrc = fileURLToPath(
new URL("../lib-aifirst-ui/src", import.meta.url),
)
const lexicalRagUiSrc = fileURLToPath(
new URL("../lib-lexical-rag-ui/src", import.meta.url),
)
const notificationUiSrc = fileURLToPath(
new URL("../lib-notification-ui/src", import.meta.url),
)
const onboardingUiSrc = fileURLToPath(
new URL("../lib-onboarding-ui/src", import.meta.url),
)
// Sibling lib packages (lib-content-ui, lib-content-editor-ui) import bare
// deps like clsx and @tiptap/* but have no node_modules of their own. Pin
@@ -105,6 +126,8 @@ const aliasedDeps = [
"@tiptap/extension-placeholder",
"@tiptap/extension-image",
"minisearch",
"react-markdown",
"remark-gfm",
]
const sharedDepAliases = Object.fromEntries(
aliasedDeps.map((name) => [name, resolvePath(nodeModules, name)]),
@@ -119,36 +142,64 @@ const dedupeDeps = [
export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
resolve: {
alias: {
"@crema/content-ui": `${contentUiSrc}/index.ts`,
"@crema/content-editor-ui": `${contentEditorUiSrc}/index.ts`,
"@crema/content-media-ui": `${contentMediaUiSrc}/index.tsx`,
"@crema/color-ui": `${colorUiSrc}/index.tsx`,
"@crema/typography-ui": `${typographyUiSrc}/index.tsx`,
"@crema/data-ui": `${dataUiSrc}/index.tsx`,
"@crema/layout-ui": `${layoutUiSrc}/index.tsx`,
"@crema/map-ui": `${mapUiSrc}/index.tsx`,
"@crema/form-ui": `${formUiSrc}/index.tsx`,
"@crema/feedback-ui": `${feedbackUiSrc}/index.tsx`,
"@crema/diagram-ui": `${diagramUiSrc}/index.tsx`,
"@crema/chat-ui": `${chatUiSrc}/index.tsx`,
"@crema/calendar-ui": `${calendarUiSrc}/index.tsx`,
"@crema/code-ui": `${codeUiSrc}/index.tsx`,
"@crema/ai-ui": `${aiUiSrc}/index.tsx`,
"@crema/auth-ui": `${authUiSrc}/index.tsx`,
"@crema/table-ui": `${tableUiSrc}/index.tsx`,
"@crema/search-ui": `${searchUiSrc}/index.tsx`,
"@crema/arcadia-client": `${arcadiaClientSrc}/index.tsx`,
"@crema/arcadia-auth-ui": `${arcadiaAuthUiSrc}/index.tsx`,
"@crema/llm-ui": `${llmUiSrc}/index.tsx`,
"@crema/llm-providers-ui": `${llmProvidersUiSrc}/index.tsx`,
"@crema/file-ui": `${fileUiSrc}/index.tsx`,
"@crema/card-ui": `${cardUiSrc}/index.tsx`,
"@crema/dashboard-ui": `${dashboardUiSrc}/index.tsx`,
"@crema/chart-ui": `${chartUiSrc}/index.tsx`,
"@crema/status-ui": `${statusUiSrc}/index.tsx`,
...sharedDepAliases,
},
// Array form so we can express both the bare-specifier alias
// (`@crema/agent-ui` -> src/index.tsx) and a subpath prefix alias
// (`@crema/agent-ui/chat` -> src/chat) for libs whose sibling-lib
// code reaches into deeper modules. Prefix entries with regex `find`
// are matched first.
alias: [
// Subpath prefixes — longest first so they win before the bare match.
{ find: /^@crema\/agent-ui\//, replacement: `${agentUiSrc}/` },
{ find: /^@crema\/aifirst-ui\//, replacement: `${aifirstUiSrc}/` },
{ find: /^@crema\/notification-ui\//, replacement: `${notificationUiSrc}/` },
{ find: /^@crema\/onboarding-ui\//, replacement: `${onboardingUiSrc}/` },
{ find: /^@crema\/lexical-rag-ui\//, replacement: `${lexicalRagUiSrc}/` },
{ find: /^@crema\/action-bus\//, replacement: `${actionBusSrc}/` },
// Bare-specifier exact matches.
{ find: "@crema/content-ui", replacement: `${contentUiSrc}/index.ts` },
{ find: "@crema/content-editor-ui", replacement: `${contentEditorUiSrc}/index.ts` },
{ find: "@crema/content-media-ui", replacement: `${contentMediaUiSrc}/index.tsx` },
{ find: "@crema/color-ui", replacement: `${colorUiSrc}/index.tsx` },
{ find: "@crema/typography-ui", replacement: `${typographyUiSrc}/index.tsx` },
{ find: "@crema/data-ui", replacement: `${dataUiSrc}/index.tsx` },
{ find: "@crema/layout-ui", replacement: `${layoutUiSrc}/index.tsx` },
{ find: "@crema/map-ui", replacement: `${mapUiSrc}/index.tsx` },
{ find: "@crema/form-ui", replacement: `${formUiSrc}/index.tsx` },
{ find: "@crema/feedback-ui", replacement: `${feedbackUiSrc}/index.tsx` },
{ find: "@crema/diagram-ui", replacement: `${diagramUiSrc}/index.tsx` },
{ find: "@crema/chat-ui", replacement: `${chatUiSrc}/index.tsx` },
{ find: "@crema/calendar-ui", replacement: `${calendarUiSrc}/index.tsx` },
{ find: "@crema/code-ui", replacement: `${codeUiSrc}/index.tsx` },
{ find: "@crema/ai-ui", replacement: `${aiUiSrc}/index.tsx` },
{ find: "@crema/auth-ui", replacement: `${authUiSrc}/index.tsx` },
{ find: "@crema/table-ui", replacement: `${tableUiSrc}/index.tsx` },
{ find: "@crema/search-ui", replacement: `${searchUiSrc}/index.tsx` },
{ find: "@crema/arcadia-client", replacement: `${arcadiaClientSrc}/index.tsx` },
{
find: "@crema/integration-registry-client",
replacement: `${integrationRegistryClientSrc}/index.tsx`,
},
{ find: "@crema/arcadia-auth-ui", replacement: `${arcadiaAuthUiSrc}/index.tsx` },
{ find: "@crema/llm-ui", replacement: `${llmUiSrc}/index.tsx` },
{ find: "@crema/llm-providers-ui", replacement: `${llmProvidersUiSrc}/index.tsx` },
{ find: "@crema/file-ui", replacement: `${fileUiSrc}/index.tsx` },
{ find: "@crema/card-ui", replacement: `${cardUiSrc}/index.tsx` },
{ find: "@crema/dashboard-ui", replacement: `${dashboardUiSrc}/index.tsx` },
{ find: "@crema/chart-ui", replacement: `${chartUiSrc}/index.tsx` },
{ find: "@crema/status-ui", replacement: `${statusUiSrc}/index.tsx` },
{ find: "@crema/action-bus", replacement: `${actionBusSrc}/index.tsx` },
{ find: "@crema/agent-ui", replacement: `${agentUiSrc}/index.tsx` },
{ find: "@crema/aifirst-ui", replacement: `${aifirstUiSrc}/index.tsx` },
{ find: "@crema/lexical-rag-ui", replacement: `${lexicalRagUiSrc}/index.tsx` },
{ find: "@crema/notification-ui", replacement: `${notificationUiSrc}/index.tsx` },
{ find: "@crema/onboarding-ui", replacement: `${onboardingUiSrc}/index.tsx` },
...Object.entries(sharedDepAliases).map(([find, replacement]) => ({
find,
replacement,
})),
],
dedupe: dedupeDeps,
},
// Pre-bundle deps that sibling libs reach for. Without this, Vite