diff --git a/apps/cloud/package.json b/apps/cloud/package.json index 8abbe215f..e6350881e 100644 --- a/apps/cloud/package.json +++ b/apps/cloud/package.json @@ -79,6 +79,7 @@ "@executor-js/cli": "workspace:*", "@rhyssul/portless": "^0.13.0", "@tailwindcss/vite": "catalog:", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", diff --git a/apps/cloud/scripts/build.mjs b/apps/cloud/scripts/build.mjs index 57b58a12b..aeae418e3 100644 --- a/apps/cloud/scripts/build.mjs +++ b/apps/cloud/scripts/build.mjs @@ -18,7 +18,13 @@ console.log(`[build] VITE_PUBLIC_ANALYTICS_PATH=${process.env.VITE_PUBLIC_ANALYT rmSync(new URL("../dist/", import.meta.url), { force: true, recursive: true }); -const steps = ["turbo run build --filter @executor-js/vite-plugin", "vite build"]; +const steps = [ + // Workspace packages whose exports app code (or this very vite config) + // resolves from dist under Node — vite's config loader externalizes bare + // imports, so they must be built before vite starts. + "turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react", + "vite build", +]; for (const step of steps) { const result = spawnSync(step, { stdio: "inherit", shell: true }); diff --git a/apps/cloud/src/routeTree.gen.ts b/apps/cloud/src/routeTree.gen.ts index 96a9354e5..3cbc16262 100644 --- a/apps/cloud/src/routeTree.gen.ts +++ b/apps/cloud/src/routeTree.gen.ts @@ -9,25 +9,26 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' -import { Route as ToolsRouteImport } from './routes/tools' -import { Route as SetupMcpRouteImport } from './routes/setup-mcp' -import { Route as SecretsRouteImport } from './routes/secrets' -import { Route as PoliciesRouteImport } from './routes/policies' -import { Route as OrgRouteImport } from './routes/org' -import { Route as CreateOrgRouteImport } from './routes/create-org' -import { Route as BillingRouteImport } from './routes/billing' -import { Route as ApiKeysRouteImport } from './routes/api-keys' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ResumeExecutionIdRouteImport } from './routes/resume.$executionId' -import { Route as IntegrationsNamespaceRouteImport } from './routes/integrations.$namespace' -import { Route as BillingPlansRouteImport } from './routes/billing_.plans' -import { Route as IntegrationsAddPluginKeyRouteImport } from './routes/integrations.add.$pluginKey' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport } from './../../../packages/react/src/routes/tools' +import { Route as SetupMcpRouteImport } from './routes/app/setup-mcp' +import { Route as SecretsRouteImport } from './routes/app/secrets' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport } from './../../../packages/react/src/routes/policies' +import { Route as OrgRouteImport } from './routes/app/org' +import { Route as CreateOrgRouteImport } from './routes/app/create-org' +import { Route as BillingRouteImport } from './routes/app/billing' +import { Route as ApiKeysRouteImport } from './routes/app/api-keys' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport } from './../../../packages/react/src/routes/index' +import { Route as ResumeDotexecutionIdRouteImport } from './routes/app/resume.$executionId' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport } from './../../../packages/react/src/routes/integrations.$namespace' +import { Route as Billing_DotplansRouteImport } from './routes/app/billing_.plans' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport } from './../../../packages/react/src/routes/integrations.add.$pluginKey' -const ToolsRoute = ToolsRouteImport.update({ - id: '/tools', - path: '/tools', - getParentRoute: () => rootRouteImport, -} as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport.update({ + id: '/tools', + path: '/tools', + getParentRoute: () => rootRouteImport, + } as any) const SetupMcpRoute = SetupMcpRouteImport.update({ id: '/setup-mcp', path: '/setup-mcp', @@ -38,11 +39,12 @@ const SecretsRoute = SecretsRouteImport.update({ path: '/secrets', getParentRoute: () => rootRouteImport, } as any) -const PoliciesRoute = PoliciesRouteImport.update({ - id: '/policies', - path: '/policies', - getParentRoute: () => rootRouteImport, -} as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport.update({ + id: '/policies', + path: '/policies', + getParentRoute: () => rootRouteImport, + } as any) const OrgRoute = OrgRouteImport.update({ id: '/org', path: '/org', @@ -63,78 +65,84 @@ const ApiKeysRoute = ApiKeysRouteImport.update({ path: '/api-keys', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ResumeExecutionIdRoute = ResumeExecutionIdRouteImport.update({ +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, + } as any) +const ResumeDotexecutionIdRoute = ResumeDotexecutionIdRouteImport.update({ id: '/resume/$executionId', path: '/resume/$executionId', getParentRoute: () => rootRouteImport, } as any) -const IntegrationsNamespaceRoute = IntegrationsNamespaceRouteImport.update({ - id: '/integrations/$namespace', - path: '/integrations/$namespace', - getParentRoute: () => rootRouteImport, -} as any) -const BillingPlansRoute = BillingPlansRouteImport.update({ +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport.update( + { + id: '/integrations/$namespace', + path: '/integrations/$namespace', + getParentRoute: () => rootRouteImport, + } as any, + ) +const Billing_DotplansRoute = Billing_DotplansRouteImport.update({ id: '/billing_/plans', path: '/billing/plans', getParentRoute: () => rootRouteImport, } as any) -const IntegrationsAddPluginKeyRoute = - IntegrationsAddPluginKeyRouteImport.update({ - id: '/integrations/add/$pluginKey', - path: '/integrations/add/$pluginKey', - getParentRoute: () => rootRouteImport, - } as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport.update( + { + id: '/integrations/add/$pluginKey', + path: '/integrations/add/$pluginKey', + getParentRoute: () => rootRouteImport, + } as any, + ) export interface FileRoutesByFullPath { - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/api-keys': typeof ApiKeysRoute '/billing': typeof BillingRoute '/create-org': typeof CreateOrgRoute '/org': typeof OrgRoute - '/policies': typeof PoliciesRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute '/setup-mcp': typeof SetupMcpRoute - '/tools': typeof ToolsRoute - '/billing/plans': typeof BillingPlansRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/billing/plans': typeof Billing_DotplansRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof ResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute } export interface FileRoutesByTo { - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/api-keys': typeof ApiKeysRoute '/billing': typeof BillingRoute '/create-org': typeof CreateOrgRoute '/org': typeof OrgRoute - '/policies': typeof PoliciesRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute '/setup-mcp': typeof SetupMcpRoute - '/tools': typeof ToolsRoute - '/billing/plans': typeof BillingPlansRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/billing/plans': typeof Billing_DotplansRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof ResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute } export interface FileRoutesById { __root__: typeof rootRouteImport - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/api-keys': typeof ApiKeysRoute '/billing': typeof BillingRoute '/create-org': typeof CreateOrgRoute '/org': typeof OrgRoute - '/policies': typeof PoliciesRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute '/setup-mcp': typeof SetupMcpRoute - '/tools': typeof ToolsRoute - '/billing_/plans': typeof BillingPlansRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/billing_/plans': typeof Billing_DotplansRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof ResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -185,19 +193,19 @@ export interface FileRouteTypes { fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute ApiKeysRoute: typeof ApiKeysRoute BillingRoute: typeof BillingRoute CreateOrgRoute: typeof CreateOrgRoute OrgRoute: typeof OrgRoute - PoliciesRoute: typeof PoliciesRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute SecretsRoute: typeof SecretsRoute SetupMcpRoute: typeof SetupMcpRoute - ToolsRoute: typeof ToolsRoute - BillingPlansRoute: typeof BillingPlansRoute - IntegrationsNamespaceRoute: typeof IntegrationsNamespaceRoute - ResumeExecutionIdRoute: typeof ResumeExecutionIdRoute - IntegrationsAddPluginKeyRoute: typeof IntegrationsAddPluginKeyRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + Billing_DotplansRoute: typeof Billing_DotplansRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + ResumeDotexecutionIdRoute: typeof ResumeDotexecutionIdRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute } declare module '@tanstack/react-router' { @@ -206,7 +214,7 @@ declare module '@tanstack/react-router' { id: '/tools' path: '/tools' fullPath: '/tools' - preLoaderRoute: typeof ToolsRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport parentRoute: typeof rootRouteImport } '/setup-mcp': { @@ -227,7 +235,7 @@ declare module '@tanstack/react-router' { id: '/policies' path: '/policies' fullPath: '/policies' - preLoaderRoute: typeof PoliciesRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport parentRoute: typeof rootRouteImport } '/org': { @@ -262,54 +270,59 @@ declare module '@tanstack/react-router' { id: '/' path: '/' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport parentRoute: typeof rootRouteImport } '/resume/$executionId': { id: '/resume/$executionId' path: '/resume/$executionId' fullPath: '/resume/$executionId' - preLoaderRoute: typeof ResumeExecutionIdRouteImport + preLoaderRoute: typeof ResumeDotexecutionIdRouteImport parentRoute: typeof rootRouteImport } '/integrations/$namespace': { id: '/integrations/$namespace' path: '/integrations/$namespace' fullPath: '/integrations/$namespace' - preLoaderRoute: typeof IntegrationsNamespaceRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport parentRoute: typeof rootRouteImport } '/billing_/plans': { id: '/billing_/plans' path: '/billing/plans' fullPath: '/billing/plans' - preLoaderRoute: typeof BillingPlansRouteImport + preLoaderRoute: typeof Billing_DotplansRouteImport parentRoute: typeof rootRouteImport } '/integrations/add/$pluginKey': { id: '/integrations/add/$pluginKey' path: '/integrations/add/$pluginKey' fullPath: '/integrations/add/$pluginKey' - preLoaderRoute: typeof IntegrationsAddPluginKeyRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute, ApiKeysRoute: ApiKeysRoute, BillingRoute: BillingRoute, CreateOrgRoute: CreateOrgRoute, OrgRoute: OrgRoute, - PoliciesRoute: PoliciesRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute, SecretsRoute: SecretsRoute, SetupMcpRoute: SetupMcpRoute, - ToolsRoute: ToolsRoute, - BillingPlansRoute: BillingPlansRoute, - IntegrationsNamespaceRoute: IntegrationsNamespaceRoute, - ResumeExecutionIdRoute: ResumeExecutionIdRoute, - IntegrationsAddPluginKeyRoute: IntegrationsAddPluginKeyRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute, + Billing_DotplansRoute: Billing_DotplansRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute, + ResumeDotexecutionIdRoute: ResumeDotexecutionIdRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/apps/cloud/src/routes/api-keys.tsx b/apps/cloud/src/routes/app/api-keys.tsx similarity index 100% rename from apps/cloud/src/routes/api-keys.tsx rename to apps/cloud/src/routes/app/api-keys.tsx diff --git a/apps/cloud/src/routes/billing.tsx b/apps/cloud/src/routes/app/billing.tsx similarity index 100% rename from apps/cloud/src/routes/billing.tsx rename to apps/cloud/src/routes/app/billing.tsx diff --git a/apps/cloud/src/routes/billing_.plans.tsx b/apps/cloud/src/routes/app/billing_.plans.tsx similarity index 100% rename from apps/cloud/src/routes/billing_.plans.tsx rename to apps/cloud/src/routes/app/billing_.plans.tsx diff --git a/apps/cloud/src/routes/create-org.tsx b/apps/cloud/src/routes/app/create-org.tsx similarity index 70% rename from apps/cloud/src/routes/create-org.tsx rename to apps/cloud/src/routes/app/create-org.tsx index e56083a03..8a8e6133d 100644 --- a/apps/cloud/src/routes/create-org.tsx +++ b/apps/cloud/src/routes/app/create-org.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { CreateOrgPage } from "../web/pages/create-org"; +import { CreateOrgPage } from "../../web/pages/create-org"; export const Route = createFileRoute("/create-org")({ component: CreateOrgPage, diff --git a/apps/cloud/src/routes/org.tsx b/apps/cloud/src/routes/app/org.tsx similarity index 99% rename from apps/cloud/src/routes/org.tsx rename to apps/cloud/src/routes/app/org.tsx index 0f0c336bd..39a57774b 100644 --- a/apps/cloud/src/routes/org.tsx +++ b/apps/cloud/src/routes/app/org.tsx @@ -15,7 +15,7 @@ import { DropdownMenuTrigger, } from "@executor-js/react/components/dropdown-menu"; import { OrgPage as SharedOrgPage } from "@executor-js/react/pages/org"; -import { orgDomainsAtom, getDomainVerificationLink, deleteDomain } from "../web/org-atoms"; +import { orgDomainsAtom, getDomainVerificationLink, deleteDomain } from "../../web/org-atoms"; // --------------------------------------------------------------------------- // Cloud organization page. The members / roles / invite / org-name surface is diff --git a/apps/cloud/src/routes/resume.$executionId.tsx b/apps/cloud/src/routes/app/resume.$executionId.tsx similarity index 95% rename from apps/cloud/src/routes/resume.$executionId.tsx rename to apps/cloud/src/routes/app/resume.$executionId.tsx index 17dec4d06..d9d8eccc3 100644 --- a/apps/cloud/src/routes/resume.$executionId.tsx +++ b/apps/cloud/src/routes/app/resume.$executionId.tsx @@ -7,7 +7,7 @@ import { ResumeApprovalPageView, } from "@executor-js/react/pages/resume-approval"; -import { mcpPausedExecutionAtom, resumeMcpExecution } from "../web/mcp-resume-atoms"; +import { mcpPausedExecutionAtom, resumeMcpExecution } from "../../web/mcp-resume-atoms"; const SearchParams = Schema.toStandardSchemaV1( Schema.Struct({ diff --git a/apps/cloud/src/routes/secrets.tsx b/apps/cloud/src/routes/app/secrets.tsx similarity index 100% rename from apps/cloud/src/routes/secrets.tsx rename to apps/cloud/src/routes/app/secrets.tsx diff --git a/apps/cloud/src/routes/setup-mcp.tsx b/apps/cloud/src/routes/app/setup-mcp.tsx similarity index 71% rename from apps/cloud/src/routes/setup-mcp.tsx rename to apps/cloud/src/routes/app/setup-mcp.tsx index 3af902d9f..b249fbfbf 100644 --- a/apps/cloud/src/routes/setup-mcp.tsx +++ b/apps/cloud/src/routes/app/setup-mcp.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { SetupMcpPage } from "../web/pages/setup-mcp"; +import { SetupMcpPage } from "../../web/pages/setup-mcp"; export const Route = createFileRoute("/setup-mcp")({ component: SetupMcpPage, diff --git a/apps/cloud/src/routes/index.tsx b/apps/cloud/src/routes/index.tsx deleted file mode 100644 index 2d57f82f0..000000000 --- a/apps/cloud/src/routes/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationsPage } from "@executor-js/react/pages/integrations"; - -export const Route = createFileRoute("/")({ - component: IntegrationsPage, -}); diff --git a/apps/cloud/src/routes/integrations.$namespace.tsx b/apps/cloud/src/routes/integrations.$namespace.tsx deleted file mode 100644 index 49a458104..000000000 --- a/apps/cloud/src/routes/integrations.$namespace.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationDetailPage } from "@executor-js/react/pages/integration-detail"; - -export const Route = createFileRoute("/integrations/$namespace")({ - component: () => { - const { namespace } = Route.useParams(); - return ; - }, -}); diff --git a/apps/cloud/src/routes/integrations.add.$pluginKey.tsx b/apps/cloud/src/routes/integrations.add.$pluginKey.tsx deleted file mode 100644 index cdf2b8a8a..000000000 --- a/apps/cloud/src/routes/integrations.add.$pluginKey.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Schema } from "effect"; -import { createFileRoute } from "@tanstack/react-router"; -import { AddIntegrationPage } from "@executor-js/react/pages/integration-add"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - url: Schema.optional(Schema.String), - preset: Schema.optional(Schema.String), - namespace: Schema.optional(Schema.String), - }), -); - -export const Route = createFileRoute("/integrations/add/$pluginKey")({ - validateSearch: SearchParams, - component: () => { - const { pluginKey } = Route.useParams(); - const { url, preset, namespace } = Route.useSearch(); - return ( - - ); - }, -}); diff --git a/apps/cloud/src/routes/policies.tsx b/apps/cloud/src/routes/policies.tsx deleted file mode 100644 index a9de9ff6f..000000000 --- a/apps/cloud/src/routes/policies.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { PoliciesPage } from "@executor-js/react/pages/policies"; - -export const Route = createFileRoute("/policies")({ - component: () => , -}); diff --git a/apps/cloud/src/routes/tools.tsx b/apps/cloud/src/routes/tools.tsx deleted file mode 100644 index 25929fd2b..000000000 --- a/apps/cloud/src/routes/tools.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { ToolsPage } from "@executor-js/react/pages/tools"; - -export const Route = createFileRoute("/tools")({ - component: ToolsPage, -}); diff --git a/apps/cloud/vite.config.ts b/apps/cloud/vite.config.ts index 72fd75d69..a8b453cab 100644 --- a/apps/cloud/vite.config.ts +++ b/apps/cloud/vite.config.ts @@ -2,6 +2,8 @@ import { fileURLToPath } from "node:url"; import { defineConfig, loadEnv, type Plugin } from "vite"; import { cloudflare } from "@cloudflare/vite-plugin"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { physical, rootRoute } from "@tanstack/virtual-file-routes"; +import { consoleRoutes } from "@executor-js/react/console-routes"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; import executorVitePlugin from "@executor-js/vite-plugin"; @@ -67,7 +69,24 @@ export default defineConfig(({ mode }) => { tailwindcss(), executorVitePlugin(), cloudflare({ viteEnvironment: { name: "ssr" }, inspectorPort: false }), - tanstackStart(), + tanstackStart({ + // Shared console routes come from @executor-js/react (see its + // console-routes.ts); cloud owns its root (WorkOS auth + billing + // shell) and the cloud-specific routes under src/routes/app. + // Excluded shared paths are intentional divergence: cloud's + // /secrets redirects to / (credential storage is product plumbing + // here), its /resume page is the cloud variant, and client plugin + // pages aren't wired up on cloud. + router: { + virtualRouteConfig: rootRoute("__root.tsx", [ + ...consoleRoutes({ + dir: "../../../../packages/react/src/routes", + exclude: ["/secrets", "/resume/$executionId", "/plugins/$pluginId/$"], + }), + physical("", "app"), + ]), + }, + }), react(), ], }; diff --git a/apps/host-selfhost/package.json b/apps/host-selfhost/package.json index 11726031c..f52a6b019 100644 --- a/apps/host-selfhost/package.json +++ b/apps/host-selfhost/package.json @@ -47,6 +47,7 @@ "@executor-js/vite-plugin": "workspace:*", "@tailwindcss/vite": "catalog:", "@tanstack/router-plugin": "^1.167.12", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", diff --git a/apps/host-selfhost/vite.config.ts b/apps/host-selfhost/vite.config.ts index d921ecb74..7c5bafad9 100644 --- a/apps/host-selfhost/vite.config.ts +++ b/apps/host-selfhost/vite.config.ts @@ -5,6 +5,8 @@ import { defineConfig, type Plugin } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; +import { physical, rootRoute } from "@tanstack/virtual-file-routes"; +import { consoleRoutes } from "@executor-js/react/console-routes"; import executorVitePlugin from "@executor-js/vite-plugin"; // Self-host web SPA. Mirrors @executor-js/app's vite plugin bundle, but points @@ -134,6 +136,14 @@ export default defineConfig({ autoCodeSplitting: true, routesDirectory: fileURLToPath(new URL("./web/routes", import.meta.url)), generatedRouteTree: fileURLToPath(new URL("./web/routeTree.gen.ts", import.meta.url)), + // Shared console routes come from @executor-js/react (see its + // console-routes.ts); this app owns its root (the Better Auth shell) + // and the self-host-specific routes under web/routes/app (admin, + // api-keys, invite join). + virtualRouteConfig: rootRoute("__root.tsx", [ + ...consoleRoutes({ dir: "../../../../packages/react/src/routes" }), + physical("", "app"), + ]), }), ...react(), ], diff --git a/apps/host-selfhost/web/routeTree.gen.ts b/apps/host-selfhost/web/routeTree.gen.ts index a53ccbec3..4c5e12f7a 100644 --- a/apps/host-selfhost/web/routeTree.gen.ts +++ b/apps/host-selfhost/web/routeTree.gen.ts @@ -9,35 +9,36 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' -import { Route as ToolsRouteImport } from './routes/tools' -import { Route as SecretsRouteImport } from './routes/secrets' -import { Route as PoliciesRouteImport } from './routes/policies' -import { Route as ApiKeysRouteImport } from './routes/api-keys' -import { Route as AdminRouteImport } from './routes/admin' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SourcesNamespaceRouteImport } from './routes/sources.$namespace' -import { Route as ResumeExecutionIdRouteImport } from './routes/resume.$executionId' -import { Route as JoinCodeRouteImport } from './routes/join.$code' -import { Route as IntegrationsNamespaceRouteImport } from './routes/integrations.$namespace' -import { Route as SourcesAddPluginKeyRouteImport } from './routes/sources.add.$pluginKey' -import { Route as PluginsPluginIdSplatRouteImport } from './routes/plugins.$pluginId.$' -import { Route as IntegrationsAddPluginKeyRouteImport } from './routes/integrations.add.$pluginKey' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport } from './../../../packages/react/src/routes/tools' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRouteImport } from './../../../packages/react/src/routes/secrets' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport } from './../../../packages/react/src/routes/policies' +import { Route as ApiKeysRouteImport } from './routes/app/api-keys' +import { Route as AdminRouteImport } from './routes/app/admin' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport } from './../../../packages/react/src/routes/index' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRouteImport } from './../../../packages/react/src/routes/resume.$executionId' +import { Route as JoinDotcodeRouteImport } from './routes/app/join.$code' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport } from './../../../packages/react/src/routes/integrations.$namespace' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport } from './../../../packages/react/src/routes/plugins.$pluginId.$' +import { Route as DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport } from './../../../packages/react/src/routes/integrations.add.$pluginKey' -const ToolsRoute = ToolsRouteImport.update({ - id: '/tools', - path: '/tools', - getParentRoute: () => rootRouteImport, -} as any) -const SecretsRoute = SecretsRouteImport.update({ - id: '/secrets', - path: '/secrets', - getParentRoute: () => rootRouteImport, -} as any) -const PoliciesRoute = PoliciesRouteImport.update({ - id: '/policies', - path: '/policies', - getParentRoute: () => rootRouteImport, -} as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport.update({ + id: '/tools', + path: '/tools', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRouteImport.update({ + id: '/secrets', + path: '/secrets', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport.update({ + id: '/policies', + path: '/policies', + getParentRoute: () => rootRouteImport, + } as any) const ApiKeysRoute = ApiKeysRouteImport.update({ id: '/api-keys', path: '/api-keys', @@ -48,93 +49,89 @@ const AdminRoute = AdminRouteImport.update({ path: '/admin', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const SourcesNamespaceRoute = SourcesNamespaceRouteImport.update({ - id: '/sources/$namespace', - path: '/sources/$namespace', - getParentRoute: () => rootRouteImport, -} as any) -const ResumeExecutionIdRoute = ResumeExecutionIdRouteImport.update({ - id: '/resume/$executionId', - path: '/resume/$executionId', - getParentRoute: () => rootRouteImport, -} as any) -const JoinCodeRoute = JoinCodeRouteImport.update({ +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRouteImport.update( + { + id: '/resume/$executionId', + path: '/resume/$executionId', + getParentRoute: () => rootRouteImport, + } as any, + ) +const JoinDotcodeRoute = JoinDotcodeRouteImport.update({ id: '/join/$code', path: '/join/$code', getParentRoute: () => rootRouteImport, } as any) -const IntegrationsNamespaceRoute = IntegrationsNamespaceRouteImport.update({ - id: '/integrations/$namespace', - path: '/integrations/$namespace', - getParentRoute: () => rootRouteImport, -} as any) -const SourcesAddPluginKeyRoute = SourcesAddPluginKeyRouteImport.update({ - id: '/sources/add/$pluginKey', - path: '/sources/add/$pluginKey', - getParentRoute: () => rootRouteImport, -} as any) -const PluginsPluginIdSplatRoute = PluginsPluginIdSplatRouteImport.update({ - id: '/plugins/$pluginId/$', - path: '/plugins/$pluginId/$', - getParentRoute: () => rootRouteImport, -} as any) -const IntegrationsAddPluginKeyRoute = - IntegrationsAddPluginKeyRouteImport.update({ - id: '/integrations/add/$pluginKey', - path: '/integrations/add/$pluginKey', - getParentRoute: () => rootRouteImport, - } as any) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport.update( + { + id: '/integrations/$namespace', + path: '/integrations/$namespace', + getParentRoute: () => rootRouteImport, + } as any, + ) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport.update( + { + id: '/plugins/$pluginId/$', + path: '/plugins/$pluginId/$', + getParentRoute: () => rootRouteImport, + } as any, + ) +const DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute = + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport.update( + { + id: '/integrations/add/$pluginKey', + path: '/integrations/add/$pluginKey', + getParentRoute: () => rootRouteImport, + } as any, + ) export interface FileRoutesByFullPath { - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/admin': typeof AdminRoute '/api-keys': typeof ApiKeysRoute - '/policies': typeof PoliciesRoute - '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/join/$code': typeof JoinCodeRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/sources/$namespace': typeof SourcesNamespaceRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute - '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute + '/secrets': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/join/$code': typeof JoinDotcodeRoute + '/resume/$executionId': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRoutesByTo { - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/admin': typeof AdminRoute '/api-keys': typeof ApiKeysRoute - '/policies': typeof PoliciesRoute - '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/join/$code': typeof JoinCodeRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/sources/$namespace': typeof SourcesNamespaceRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute - '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute + '/secrets': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/join/$code': typeof JoinDotcodeRoute + '/resume/$executionId': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport - '/': typeof IndexRoute + '/': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute '/admin': typeof AdminRoute '/api-keys': typeof ApiKeysRoute - '/policies': typeof PoliciesRoute - '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/join/$code': typeof JoinCodeRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/sources/$namespace': typeof SourcesNamespaceRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute - '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute + '/policies': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute + '/secrets': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute + '/tools': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + '/join/$code': typeof JoinDotcodeRoute + '/resume/$executionId': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -148,10 +145,8 @@ export interface FileRouteTypes { | '/integrations/$namespace' | '/join/$code' | '/resume/$executionId' - | '/sources/$namespace' | '/integrations/add/$pluginKey' | '/plugins/$pluginId/$' - | '/sources/add/$pluginKey' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -163,10 +158,8 @@ export interface FileRouteTypes { | '/integrations/$namespace' | '/join/$code' | '/resume/$executionId' - | '/sources/$namespace' | '/integrations/add/$pluginKey' | '/plugins/$pluginId/$' - | '/sources/add/$pluginKey' id: | '__root__' | '/' @@ -178,26 +171,22 @@ export interface FileRouteTypes { | '/integrations/$namespace' | '/join/$code' | '/resume/$executionId' - | '/sources/$namespace' | '/integrations/add/$pluginKey' | '/plugins/$pluginId/$' - | '/sources/add/$pluginKey' fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute AdminRoute: typeof AdminRoute ApiKeysRoute: typeof ApiKeysRoute - PoliciesRoute: typeof PoliciesRoute - SecretsRoute: typeof SecretsRoute - ToolsRoute: typeof ToolsRoute - IntegrationsNamespaceRoute: typeof IntegrationsNamespaceRoute - JoinCodeRoute: typeof JoinCodeRoute - ResumeExecutionIdRoute: typeof ResumeExecutionIdRoute - SourcesNamespaceRoute: typeof SourcesNamespaceRoute - IntegrationsAddPluginKeyRoute: typeof IntegrationsAddPluginKeyRoute - PluginsPluginIdSplatRoute: typeof PluginsPluginIdSplatRoute - SourcesAddPluginKeyRoute: typeof SourcesAddPluginKeyRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute + JoinDotcodeRoute: typeof JoinDotcodeRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute } declare module '@tanstack/react-router' { @@ -206,21 +195,21 @@ declare module '@tanstack/react-router' { id: '/tools' path: '/tools' fullPath: '/tools' - preLoaderRoute: typeof ToolsRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRouteImport parentRoute: typeof rootRouteImport } '/secrets': { id: '/secrets' path: '/secrets' fullPath: '/secrets' - preLoaderRoute: typeof SecretsRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRouteImport parentRoute: typeof rootRouteImport } '/policies': { id: '/policies' path: '/policies' fullPath: '/policies' - preLoaderRoute: typeof PoliciesRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRouteImport parentRoute: typeof rootRouteImport } '/api-keys': { @@ -241,75 +230,67 @@ declare module '@tanstack/react-router' { id: '/' path: '/' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/sources/$namespace': { - id: '/sources/$namespace' - path: '/sources/$namespace' - fullPath: '/sources/$namespace' - preLoaderRoute: typeof SourcesNamespaceRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRouteImport parentRoute: typeof rootRouteImport } '/resume/$executionId': { id: '/resume/$executionId' path: '/resume/$executionId' fullPath: '/resume/$executionId' - preLoaderRoute: typeof ResumeExecutionIdRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRouteImport parentRoute: typeof rootRouteImport } '/join/$code': { id: '/join/$code' path: '/join/$code' fullPath: '/join/$code' - preLoaderRoute: typeof JoinCodeRouteImport + preLoaderRoute: typeof JoinDotcodeRouteImport parentRoute: typeof rootRouteImport } '/integrations/$namespace': { id: '/integrations/$namespace' path: '/integrations/$namespace' fullPath: '/integrations/$namespace' - preLoaderRoute: typeof IntegrationsNamespaceRouteImport - parentRoute: typeof rootRouteImport - } - '/sources/add/$pluginKey': { - id: '/sources/add/$pluginKey' - path: '/sources/add/$pluginKey' - fullPath: '/sources/add/$pluginKey' - preLoaderRoute: typeof SourcesAddPluginKeyRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRouteImport parentRoute: typeof rootRouteImport } '/plugins/$pluginId/$': { id: '/plugins/$pluginId/$' path: '/plugins/$pluginId/$' fullPath: '/plugins/$pluginId/$' - preLoaderRoute: typeof PluginsPluginIdSplatRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport parentRoute: typeof rootRouteImport } '/integrations/add/$pluginKey': { id: '/integrations/add/$pluginKey' path: '/integrations/add/$pluginKey' fullPath: '/integrations/add/$pluginKey' - preLoaderRoute: typeof IntegrationsAddPluginKeyRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIndexRoute, AdminRoute: AdminRoute, ApiKeysRoute: ApiKeysRoute, - PoliciesRoute: PoliciesRoute, - SecretsRoute: SecretsRoute, - ToolsRoute: ToolsRoute, - IntegrationsNamespaceRoute: IntegrationsNamespaceRoute, - JoinCodeRoute: JoinCodeRoute, - ResumeExecutionIdRoute: ResumeExecutionIdRoute, - SourcesNamespaceRoute: SourcesNamespaceRoute, - IntegrationsAddPluginKeyRoute: IntegrationsAddPluginKeyRoute, - PluginsPluginIdSplatRoute: PluginsPluginIdSplatRoute, - SourcesAddPluginKeyRoute: SourcesAddPluginKeyRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPoliciesRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesSecretsRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesToolsRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotnamespaceRoute, + JoinDotcodeRoute: JoinDotcodeRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesResumeDotexecutionIdRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute, + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute: + DotDotDotDotDotDotDotDotPackagesReactSrcRoutesPluginsDotpluginIdDotsplatRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/apps/host-selfhost/web/routes/admin.tsx b/apps/host-selfhost/web/routes/app/admin.tsx similarity index 99% rename from apps/host-selfhost/web/routes/admin.tsx rename to apps/host-selfhost/web/routes/app/admin.tsx index 89753082f..4009f5671 100644 --- a/apps/host-selfhost/web/routes/admin.tsx +++ b/apps/host-selfhost/web/routes/app/admin.tsx @@ -17,7 +17,7 @@ import { } from "@executor-js/react/api/account-atoms"; import { orgMemberWriteKeys } from "@executor-js/react/api/reactivity-keys"; -import { createInvite, invitesAtom, inviteWriteKeys, revokeInvite } from "../admin-atoms"; +import { createInvite, invitesAtom, inviteWriteKeys, revokeInvite } from "../../admin-atoms"; export const Route = createFileRoute("/admin")({ component: AdminPage, diff --git a/apps/host-selfhost/web/routes/api-keys.tsx b/apps/host-selfhost/web/routes/app/api-keys.tsx similarity index 100% rename from apps/host-selfhost/web/routes/api-keys.tsx rename to apps/host-selfhost/web/routes/app/api-keys.tsx diff --git a/apps/host-selfhost/web/routes/join.$code.tsx b/apps/host-selfhost/web/routes/app/join.$code.tsx similarity index 98% rename from apps/host-selfhost/web/routes/join.$code.tsx rename to apps/host-selfhost/web/routes/app/join.$code.tsx index f8394bb53..26b757f94 100644 --- a/apps/host-selfhost/web/routes/join.$code.tsx +++ b/apps/host-selfhost/web/routes/app/join.$code.tsx @@ -5,7 +5,7 @@ import { Button } from "@executor-js/react/components/button"; import { Input } from "@executor-js/react/components/input"; import { Label } from "@executor-js/react/components/label"; -import { authClient } from "../auth-client"; +import { authClient } from "../../auth-client"; export const Route = createFileRoute("/join/$code")({ component: JoinPage, diff --git a/apps/host-selfhost/web/routes/index.tsx b/apps/host-selfhost/web/routes/index.tsx deleted file mode 100644 index 2d57f82f0..000000000 --- a/apps/host-selfhost/web/routes/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationsPage } from "@executor-js/react/pages/integrations"; - -export const Route = createFileRoute("/")({ - component: IntegrationsPage, -}); diff --git a/apps/host-selfhost/web/routes/integrations.$namespace.tsx b/apps/host-selfhost/web/routes/integrations.$namespace.tsx deleted file mode 100644 index 49a458104..000000000 --- a/apps/host-selfhost/web/routes/integrations.$namespace.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationDetailPage } from "@executor-js/react/pages/integration-detail"; - -export const Route = createFileRoute("/integrations/$namespace")({ - component: () => { - const { namespace } = Route.useParams(); - return ; - }, -}); diff --git a/apps/host-selfhost/web/routes/integrations.add.$pluginKey.tsx b/apps/host-selfhost/web/routes/integrations.add.$pluginKey.tsx deleted file mode 100644 index cdf2b8a8a..000000000 --- a/apps/host-selfhost/web/routes/integrations.add.$pluginKey.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Schema } from "effect"; -import { createFileRoute } from "@tanstack/react-router"; -import { AddIntegrationPage } from "@executor-js/react/pages/integration-add"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - url: Schema.optional(Schema.String), - preset: Schema.optional(Schema.String), - namespace: Schema.optional(Schema.String), - }), -); - -export const Route = createFileRoute("/integrations/add/$pluginKey")({ - validateSearch: SearchParams, - component: () => { - const { pluginKey } = Route.useParams(); - const { url, preset, namespace } = Route.useSearch(); - return ( - - ); - }, -}); diff --git a/apps/host-selfhost/web/routes/plugins.$pluginId.$.tsx b/apps/host-selfhost/web/routes/plugins.$pluginId.$.tsx deleted file mode 100644 index 64f59a40a..000000000 --- a/apps/host-selfhost/web/routes/plugins.$pluginId.$.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { createFileRoute, notFound } from "@tanstack/react-router"; -import { useClientPlugins } from "@executor-js/sdk/client"; - -// /plugins// — mounts pages contributed by client plugins, -// materialised from `virtual:executor/plugins-client` via the root's -// ExecutorPluginsProvider. Adding a plugin to executor.config.ts is enough. - -export const Route = createFileRoute("/plugins/$pluginId/$")({ - component: PluginRouteComponent, -}); - -function normalizePath(input: string): string { - if (!input || input === "/") return "/"; - return input.startsWith("/") ? input : `/${input}`; -} - -function PluginRouteComponent() { - const { pluginId, _splat: rest } = Route.useParams(); - const plugins = useClientPlugins(); - const plugin = plugins.find((p) => p.id === pluginId); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router represents not-found from components by throwing notFound() - if (!plugin) throw notFound(); - - const target = normalizePath(rest ?? "/"); - const page = plugin.pages?.find((p) => normalizePath(p.path) === target); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router represents not-found from components by throwing notFound() - if (!page) throw notFound(); - - const Component = page.component; - return ; -} diff --git a/apps/host-selfhost/web/routes/policies.tsx b/apps/host-selfhost/web/routes/policies.tsx deleted file mode 100644 index a9de9ff6f..000000000 --- a/apps/host-selfhost/web/routes/policies.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { PoliciesPage } from "@executor-js/react/pages/policies"; - -export const Route = createFileRoute("/policies")({ - component: () => , -}); diff --git a/apps/host-selfhost/web/routes/resume.$executionId.tsx b/apps/host-selfhost/web/routes/resume.$executionId.tsx deleted file mode 100644 index 32a84347b..000000000 --- a/apps/host-selfhost/web/routes/resume.$executionId.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useCallback } from "react"; -import { useAtomSet, useAtomValue } from "@effect/atom-react"; -import { Data, Effect, Option, Schema } from "effect"; -import * as Atom from "effect/unstable/reactivity/Atom"; -import { createFileRoute } from "@tanstack/react-router"; -import { - ResumeApprovalPage, - ResumeApprovalPageView, -} from "@executor-js/react/pages/resume-approval"; -import { pausedExecutionAtom } from "@executor-js/react/api/atoms"; -import type { ElicitationAction } from "@executor-js/react/components/elicitation-approval"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - mcp_session_id: Schema.optional(Schema.String), - }), -); -const LocalMcpResumeCompleted = Schema.Struct({ - status: Schema.Literal("completed"), - text: Schema.String, - structured: Schema.Unknown, - isError: Schema.Boolean, -}); -const LocalMcpResumePaused = Schema.Struct({ - status: Schema.Literal("paused"), - text: Schema.String, - structured: Schema.Unknown, -}); -const LocalMcpResumeResult = Schema.Union([LocalMcpResumeCompleted, LocalMcpResumePaused]); -const decodeLocalMcpResumeResult = Schema.decodeUnknownOption(LocalMcpResumeResult); - -class LocalMcpResumeError extends Data.TaggedError("LocalMcpResumeError")<{ - readonly message: string; -}> {} - -type LocalMcpResumeInput = { - readonly mcpSessionId: string; - readonly executionId: string; - readonly action: ElicitationAction; - readonly content?: Record; -}; - -const resumeLocalMcpExecution = Atom.fn()((input) => - Effect.gen(function* () { - const response = yield* Effect.tryPromise({ - try: () => - fetch( - `/api/mcp-sessions/${encodeURIComponent(input.mcpSessionId)}/executions/${encodeURIComponent(input.executionId)}/resume`, - { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify( - input.action === "accept" - ? { action: input.action, content: input.content ?? {} } - : { action: input.action }, - ), - }, - ), - catch: () => new LocalMcpResumeError({ message: "Failed to submit approval." }), - }); - - if (!response.ok) { - const body = yield* Effect.tryPromise({ - try: () => response.text(), - catch: () => "", - }).pipe(Effect.orElseSucceed(() => "")); - return yield* new LocalMcpResumeError({ - message: body || `Approval request failed (${response.status}).`, - }); - } - - const body = yield* Effect.tryPromise({ - try: () => response.json(), - catch: () => new LocalMcpResumeError({ message: "Approval response was not valid JSON." }), - }); - const result = decodeLocalMcpResumeResult(body); - if (Option.isNone(result)) { - return yield* new LocalMcpResumeError({ - message: "Approval response had an unexpected shape.", - }); - } - return result.value; - }), -); - -export const Route = createFileRoute("/resume/$executionId")({ - validateSearch: SearchParams, - component: RouteComponent, -}); - -function RouteComponent() { - const { executionId } = Route.useParams(); - const { mcp_session_id: mcpSessionId } = Route.useSearch(); - if (mcpSessionId) { - return ; - } - return ; -} - -function LocalMcpResumeApproval(props: { executionId: string; mcpSessionId: string }) { - const paused = useAtomValue(pausedExecutionAtom(props.executionId)); - const doResume = useAtomSet(resumeLocalMcpExecution, { mode: "promiseExit" }); - const resume = useCallback( - (executionId: string, action: ElicitationAction, content?: Record) => - doResume({ mcpSessionId: props.mcpSessionId, executionId, action, content }), - [doResume, props.mcpSessionId], - ); - - return ( - - ); -} diff --git a/apps/host-selfhost/web/routes/secrets.tsx b/apps/host-selfhost/web/routes/secrets.tsx deleted file mode 100644 index 10bd4a10b..000000000 --- a/apps/host-selfhost/web/routes/secrets.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { SecretsPage } from "@executor-js/react/pages/secrets"; - -// The Providers/Secrets page lets self-host users inspect their credential -// backends. Credential entry happens through the per-integration Add Account -// flow (`connections.createHandoff` → `/integrations/{slug}?addAccount=1`), -// not here, so this route takes no search params. -export const Route = createFileRoute("/secrets")({ - component: () => , -}); diff --git a/apps/host-selfhost/web/routes/sources.$namespace.tsx b/apps/host-selfhost/web/routes/sources.$namespace.tsx deleted file mode 100644 index 2df401d30..000000000 --- a/apps/host-selfhost/web/routes/sources.$namespace.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createFileRoute, redirect } from "@tanstack/react-router"; - -export const Route = createFileRoute("/sources/$namespace")({ - beforeLoad: ({ params }) => { - const { namespace } = params; - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router redirects are modeled as thrown values - throw redirect({ to: "/integrations/$namespace", params: { namespace } }); - }, -}); diff --git a/apps/host-selfhost/web/routes/sources.add.$pluginKey.tsx b/apps/host-selfhost/web/routes/sources.add.$pluginKey.tsx deleted file mode 100644 index ec809b401..000000000 --- a/apps/host-selfhost/web/routes/sources.add.$pluginKey.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Schema } from "effect"; -import { createFileRoute, redirect } from "@tanstack/react-router"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - url: Schema.optional(Schema.String), - preset: Schema.optional(Schema.String), - namespace: Schema.optional(Schema.String), - }), -); - -export const Route = createFileRoute("/sources/add/$pluginKey")({ - validateSearch: SearchParams, - beforeLoad: ({ params, search }) => { - const { pluginKey } = params; - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router redirects are modeled as thrown values - throw redirect({ to: "/integrations/add/$pluginKey", params: { pluginKey }, search }); - }, -}); diff --git a/apps/host-selfhost/web/routes/tools.tsx b/apps/host-selfhost/web/routes/tools.tsx deleted file mode 100644 index 25929fd2b..000000000 --- a/apps/host-selfhost/web/routes/tools.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { ToolsPage } from "@executor-js/react/pages/tools"; - -export const Route = createFileRoute("/tools")({ - component: ToolsPage, -}); diff --git a/bun.lock b/bun.lock index 7f0e200f6..b7cd774ec 100644 --- a/bun.lock +++ b/bun.lock @@ -107,6 +107,7 @@ "@executor-js/cli": "workspace:*", "@rhyssul/portless": "^0.13.0", "@tailwindcss/vite": "catalog:", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", @@ -230,6 +231,7 @@ "@executor-js/vite-plugin": "workspace:*", "@tailwindcss/vite": "catalog:", "@tanstack/router-plugin": "^1.167.12", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", @@ -410,6 +412,7 @@ "devDependencies": { "@tailwindcss/vite": "catalog:", "@tanstack/router-plugin": "^1.167.12", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", diff --git a/packages/app/package.json b/packages/app/package.json index e14e9e288..29d38997f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -31,6 +31,7 @@ "devDependencies": { "@tailwindcss/vite": "catalog:", "@tanstack/router-plugin": "^1.167.12", + "@tanstack/virtual-file-routes": "^1.162.0", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", diff --git a/packages/app/src/routeTree.gen.ts b/packages/app/src/routeTree.gen.ts index 56432ab7a..e86a8acd1 100644 --- a/packages/app/src/routeTree.gen.ts +++ b/packages/app/src/routeTree.gen.ts @@ -9,87 +9,95 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' -import { Route as ToolsRouteImport } from './routes/tools' -import { Route as SecretsRouteImport } from './routes/secrets' -import { Route as PoliciesRouteImport } from './routes/policies' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ResumeExecutionIdRouteImport } from './routes/resume.$executionId' -import { Route as IntegrationsNamespaceRouteImport } from './routes/integrations.$namespace' -import { Route as PluginsPluginIdSplatRouteImport } from './routes/plugins.$pluginId.$' -import { Route as IntegrationsAddPluginKeyRouteImport } from './routes/integrations.add.$pluginKey' +import { Route as DotDotDotDotDotDotReactSrcRoutesToolsRouteImport } from './../../react/src/routes/tools' +import { Route as SecretsRouteImport } from './routes/app/secrets' +import { Route as DotDotDotDotDotDotReactSrcRoutesPoliciesRouteImport } from './../../react/src/routes/policies' +import { Route as DotDotDotDotDotDotReactSrcRoutesIndexRouteImport } from './../../react/src/routes/index' +import { Route as DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRouteImport } from './../../react/src/routes/resume.$executionId' +import { Route as DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRouteImport } from './../../react/src/routes/integrations.$namespace' +import { Route as DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport } from './../../react/src/routes/plugins.$pluginId.$' +import { Route as DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport } from './../../react/src/routes/integrations.add.$pluginKey' -const ToolsRoute = ToolsRouteImport.update({ - id: '/tools', - path: '/tools', - getParentRoute: () => rootRouteImport, -} as any) +const DotDotDotDotDotDotReactSrcRoutesToolsRoute = + DotDotDotDotDotDotReactSrcRoutesToolsRouteImport.update({ + id: '/tools', + path: '/tools', + getParentRoute: () => rootRouteImport, + } as any) const SecretsRoute = SecretsRouteImport.update({ id: '/secrets', path: '/secrets', getParentRoute: () => rootRouteImport, } as any) -const PoliciesRoute = PoliciesRouteImport.update({ - id: '/policies', - path: '/policies', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ResumeExecutionIdRoute = ResumeExecutionIdRouteImport.update({ - id: '/resume/$executionId', - path: '/resume/$executionId', - getParentRoute: () => rootRouteImport, -} as any) -const IntegrationsNamespaceRoute = IntegrationsNamespaceRouteImport.update({ - id: '/integrations/$namespace', - path: '/integrations/$namespace', - getParentRoute: () => rootRouteImport, -} as any) -const PluginsPluginIdSplatRoute = PluginsPluginIdSplatRouteImport.update({ - id: '/plugins/$pluginId/$', - path: '/plugins/$pluginId/$', - getParentRoute: () => rootRouteImport, -} as any) -const IntegrationsAddPluginKeyRoute = - IntegrationsAddPluginKeyRouteImport.update({ - id: '/integrations/add/$pluginKey', - path: '/integrations/add/$pluginKey', +const DotDotDotDotDotDotReactSrcRoutesPoliciesRoute = + DotDotDotDotDotDotReactSrcRoutesPoliciesRouteImport.update({ + id: '/policies', + path: '/policies', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotReactSrcRoutesIndexRoute = + DotDotDotDotDotDotReactSrcRoutesIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute = + DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRouteImport.update({ + id: '/resume/$executionId', + path: '/resume/$executionId', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute = + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRouteImport.update({ + id: '/integrations/$namespace', + path: '/integrations/$namespace', + getParentRoute: () => rootRouteImport, + } as any) +const DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute = + DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport.update({ + id: '/plugins/$pluginId/$', + path: '/plugins/$pluginId/$', getParentRoute: () => rootRouteImport, } as any) +const DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute = + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport.update( + { + id: '/integrations/add/$pluginKey', + path: '/integrations/add/$pluginKey', + getParentRoute: () => rootRouteImport, + } as any, + ) export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/policies': typeof PoliciesRoute + '/': typeof DotDotDotDotDotDotReactSrcRoutesIndexRoute + '/policies': typeof DotDotDotDotDotDotReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute + '/tools': typeof DotDotDotDotDotDotReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRoutesByTo { - '/': typeof IndexRoute - '/policies': typeof PoliciesRoute + '/': typeof DotDotDotDotDotDotReactSrcRoutesIndexRoute + '/policies': typeof DotDotDotDotDotDotReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute + '/tools': typeof DotDotDotDotDotDotReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/policies': typeof PoliciesRoute + '/': typeof DotDotDotDotDotDotReactSrcRoutesIndexRoute + '/policies': typeof DotDotDotDotDotDotReactSrcRoutesPoliciesRoute '/secrets': typeof SecretsRoute - '/tools': typeof ToolsRoute - '/integrations/$namespace': typeof IntegrationsNamespaceRoute - '/resume/$executionId': typeof ResumeExecutionIdRoute - '/integrations/add/$pluginKey': typeof IntegrationsAddPluginKeyRoute - '/plugins/$pluginId/$': typeof PluginsPluginIdSplatRoute + '/tools': typeof DotDotDotDotDotDotReactSrcRoutesToolsRoute + '/integrations/$namespace': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute + '/resume/$executionId': typeof DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute + '/integrations/add/$pluginKey': typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + '/plugins/$pluginId/$': typeof DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -125,14 +133,14 @@ export interface FileRouteTypes { fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - PoliciesRoute: typeof PoliciesRoute + DotDotDotDotDotDotReactSrcRoutesIndexRoute: typeof DotDotDotDotDotDotReactSrcRoutesIndexRoute + DotDotDotDotDotDotReactSrcRoutesPoliciesRoute: typeof DotDotDotDotDotDotReactSrcRoutesPoliciesRoute SecretsRoute: typeof SecretsRoute - ToolsRoute: typeof ToolsRoute - IntegrationsNamespaceRoute: typeof IntegrationsNamespaceRoute - ResumeExecutionIdRoute: typeof ResumeExecutionIdRoute - IntegrationsAddPluginKeyRoute: typeof IntegrationsAddPluginKeyRoute - PluginsPluginIdSplatRoute: typeof PluginsPluginIdSplatRoute + DotDotDotDotDotDotReactSrcRoutesToolsRoute: typeof DotDotDotDotDotDotReactSrcRoutesToolsRoute + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute: typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute + DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute: typeof DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute + DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute: typeof DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute } declare module '@tanstack/react-router' { @@ -141,7 +149,7 @@ declare module '@tanstack/react-router' { id: '/tools' path: '/tools' fullPath: '/tools' - preLoaderRoute: typeof ToolsRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesToolsRouteImport parentRoute: typeof rootRouteImport } '/secrets': { @@ -155,56 +163,63 @@ declare module '@tanstack/react-router' { id: '/policies' path: '/policies' fullPath: '/policies' - preLoaderRoute: typeof PoliciesRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesPoliciesRouteImport parentRoute: typeof rootRouteImport } '/': { id: '/' path: '/' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesIndexRouteImport parentRoute: typeof rootRouteImport } '/resume/$executionId': { id: '/resume/$executionId' path: '/resume/$executionId' fullPath: '/resume/$executionId' - preLoaderRoute: typeof ResumeExecutionIdRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRouteImport parentRoute: typeof rootRouteImport } '/integrations/$namespace': { id: '/integrations/$namespace' path: '/integrations/$namespace' fullPath: '/integrations/$namespace' - preLoaderRoute: typeof IntegrationsNamespaceRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRouteImport parentRoute: typeof rootRouteImport } '/plugins/$pluginId/$': { id: '/plugins/$pluginId/$' path: '/plugins/$pluginId/$' fullPath: '/plugins/$pluginId/$' - preLoaderRoute: typeof PluginsPluginIdSplatRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRouteImport parentRoute: typeof rootRouteImport } '/integrations/add/$pluginKey': { id: '/integrations/add/$pluginKey' path: '/integrations/add/$pluginKey' fullPath: '/integrations/add/$pluginKey' - preLoaderRoute: typeof IntegrationsAddPluginKeyRouteImport + preLoaderRoute: typeof DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - PoliciesRoute: PoliciesRoute, + DotDotDotDotDotDotReactSrcRoutesIndexRoute: + DotDotDotDotDotDotReactSrcRoutesIndexRoute, + DotDotDotDotDotDotReactSrcRoutesPoliciesRoute: + DotDotDotDotDotDotReactSrcRoutesPoliciesRoute, SecretsRoute: SecretsRoute, - ToolsRoute: ToolsRoute, - IntegrationsNamespaceRoute: IntegrationsNamespaceRoute, - ResumeExecutionIdRoute: ResumeExecutionIdRoute, - IntegrationsAddPluginKeyRoute: IntegrationsAddPluginKeyRoute, - PluginsPluginIdSplatRoute: PluginsPluginIdSplatRoute, + DotDotDotDotDotDotReactSrcRoutesToolsRoute: + DotDotDotDotDotDotReactSrcRoutesToolsRoute, + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute: + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotnamespaceRoute, + DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute: + DotDotDotDotDotDotReactSrcRoutesResumeDotexecutionIdRoute, + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute: + DotDotDotDotDotDotReactSrcRoutesIntegrationsDotaddDotpluginKeyRoute, + DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute: + DotDotDotDotDotDotReactSrcRoutesPluginsDotpluginIdDotsplatRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/packages/app/src/routes/secrets.tsx b/packages/app/src/routes/app/secrets.tsx similarity index 100% rename from packages/app/src/routes/secrets.tsx rename to packages/app/src/routes/app/secrets.tsx diff --git a/packages/app/src/routes/index.tsx b/packages/app/src/routes/index.tsx deleted file mode 100644 index 2d57f82f0..000000000 --- a/packages/app/src/routes/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationsPage } from "@executor-js/react/pages/integrations"; - -export const Route = createFileRoute("/")({ - component: IntegrationsPage, -}); diff --git a/packages/app/src/routes/integrations.$namespace.tsx b/packages/app/src/routes/integrations.$namespace.tsx deleted file mode 100644 index 49a458104..000000000 --- a/packages/app/src/routes/integrations.$namespace.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { IntegrationDetailPage } from "@executor-js/react/pages/integration-detail"; - -export const Route = createFileRoute("/integrations/$namespace")({ - component: () => { - const { namespace } = Route.useParams(); - return ; - }, -}); diff --git a/packages/app/src/routes/integrations.add.$pluginKey.tsx b/packages/app/src/routes/integrations.add.$pluginKey.tsx deleted file mode 100644 index baada29f3..000000000 --- a/packages/app/src/routes/integrations.add.$pluginKey.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Schema } from "effect"; -import { createFileRoute } from "@tanstack/react-router"; -import { AddIntegrationPage } from "@executor-js/react/pages/integration-add"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - url: Schema.optional(Schema.String), - preset: Schema.optional(Schema.String), - }), -); - -export const Route = createFileRoute("/integrations/add/$pluginKey")({ - validateSearch: SearchParams, - component: () => { - const { pluginKey } = Route.useParams(); - const { url, preset } = Route.useSearch(); - return ; - }, -}); diff --git a/packages/app/src/routes/plugins.$pluginId.$.tsx b/packages/app/src/routes/plugins.$pluginId.$.tsx deleted file mode 100644 index 472ab9768..000000000 --- a/packages/app/src/routes/plugins.$pluginId.$.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { createFileRoute, notFound } from "@tanstack/react-router"; -import { useClientPlugins } from "@executor-js/sdk/client"; - -// --------------------------------------------------------------------------- -// /plugins// -// -// Mounts pages contributed by client plugins. The host's -// `` (set up at the root) materialises the -// list of `ClientPluginSpec` from `virtual:executor/plugins-client`, -// and this route reads it via `useClientPlugins()` — so adding a -// plugin to `executor.config.ts` is sufficient for its pages to mount -// here, with no per-route imports. -// -// Match logic is intentionally tiny: exact path equality between the URL -// remainder and a `PageDecl.path`, with `""` and `/` treated as the -// same root. Plugins that want parameterized paths can build their own -// in-component routing for now. -// --------------------------------------------------------------------------- - -export const Route = createFileRoute("/plugins/$pluginId/$")({ - component: PluginRouteComponent, -}); - -function normalizePath(input: string): string { - if (!input || input === "/") return "/"; - return input.startsWith("/") ? input : `/${input}`; -} - -function PluginRouteComponent() { - const { pluginId, _splat: rest } = Route.useParams(); - const plugins = useClientPlugins(); - const plugin = plugins.find((p) => p.id === pluginId); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router represents not-found from components by throwing notFound() - if (!plugin) throw notFound(); - - const target = normalizePath(rest ?? "/"); - const page = plugin.pages?.find((p) => normalizePath(p.path) === target); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: TanStack Router represents not-found from components by throwing notFound() - if (!page) throw notFound(); - - const Component = page.component; - return ; -} diff --git a/packages/app/src/routes/policies.tsx b/packages/app/src/routes/policies.tsx deleted file mode 100644 index a9de9ff6f..000000000 --- a/packages/app/src/routes/policies.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { PoliciesPage } from "@executor-js/react/pages/policies"; - -export const Route = createFileRoute("/policies")({ - component: () => , -}); diff --git a/packages/app/src/routes/resume.$executionId.tsx b/packages/app/src/routes/resume.$executionId.tsx deleted file mode 100644 index 32a84347b..000000000 --- a/packages/app/src/routes/resume.$executionId.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useCallback } from "react"; -import { useAtomSet, useAtomValue } from "@effect/atom-react"; -import { Data, Effect, Option, Schema } from "effect"; -import * as Atom from "effect/unstable/reactivity/Atom"; -import { createFileRoute } from "@tanstack/react-router"; -import { - ResumeApprovalPage, - ResumeApprovalPageView, -} from "@executor-js/react/pages/resume-approval"; -import { pausedExecutionAtom } from "@executor-js/react/api/atoms"; -import type { ElicitationAction } from "@executor-js/react/components/elicitation-approval"; - -const SearchParams = Schema.toStandardSchemaV1( - Schema.Struct({ - mcp_session_id: Schema.optional(Schema.String), - }), -); -const LocalMcpResumeCompleted = Schema.Struct({ - status: Schema.Literal("completed"), - text: Schema.String, - structured: Schema.Unknown, - isError: Schema.Boolean, -}); -const LocalMcpResumePaused = Schema.Struct({ - status: Schema.Literal("paused"), - text: Schema.String, - structured: Schema.Unknown, -}); -const LocalMcpResumeResult = Schema.Union([LocalMcpResumeCompleted, LocalMcpResumePaused]); -const decodeLocalMcpResumeResult = Schema.decodeUnknownOption(LocalMcpResumeResult); - -class LocalMcpResumeError extends Data.TaggedError("LocalMcpResumeError")<{ - readonly message: string; -}> {} - -type LocalMcpResumeInput = { - readonly mcpSessionId: string; - readonly executionId: string; - readonly action: ElicitationAction; - readonly content?: Record; -}; - -const resumeLocalMcpExecution = Atom.fn()((input) => - Effect.gen(function* () { - const response = yield* Effect.tryPromise({ - try: () => - fetch( - `/api/mcp-sessions/${encodeURIComponent(input.mcpSessionId)}/executions/${encodeURIComponent(input.executionId)}/resume`, - { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify( - input.action === "accept" - ? { action: input.action, content: input.content ?? {} } - : { action: input.action }, - ), - }, - ), - catch: () => new LocalMcpResumeError({ message: "Failed to submit approval." }), - }); - - if (!response.ok) { - const body = yield* Effect.tryPromise({ - try: () => response.text(), - catch: () => "", - }).pipe(Effect.orElseSucceed(() => "")); - return yield* new LocalMcpResumeError({ - message: body || `Approval request failed (${response.status}).`, - }); - } - - const body = yield* Effect.tryPromise({ - try: () => response.json(), - catch: () => new LocalMcpResumeError({ message: "Approval response was not valid JSON." }), - }); - const result = decodeLocalMcpResumeResult(body); - if (Option.isNone(result)) { - return yield* new LocalMcpResumeError({ - message: "Approval response had an unexpected shape.", - }); - } - return result.value; - }), -); - -export const Route = createFileRoute("/resume/$executionId")({ - validateSearch: SearchParams, - component: RouteComponent, -}); - -function RouteComponent() { - const { executionId } = Route.useParams(); - const { mcp_session_id: mcpSessionId } = Route.useSearch(); - if (mcpSessionId) { - return ; - } - return ; -} - -function LocalMcpResumeApproval(props: { executionId: string; mcpSessionId: string }) { - const paused = useAtomValue(pausedExecutionAtom(props.executionId)); - const doResume = useAtomSet(resumeLocalMcpExecution, { mode: "promiseExit" }); - const resume = useCallback( - (executionId: string, action: ElicitationAction, content?: Record) => - doResume({ mcpSessionId: props.mcpSessionId, executionId, action, content }), - [doResume, props.mcpSessionId], - ); - - return ( - - ); -} diff --git a/packages/app/src/routes/tools.tsx b/packages/app/src/routes/tools.tsx deleted file mode 100644 index 25929fd2b..000000000 --- a/packages/app/src/routes/tools.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { ToolsPage } from "@executor-js/react/pages/tools"; - -export const Route = createFileRoute("/tools")({ - component: ToolsPage, -}); diff --git a/packages/app/vite.ts b/packages/app/vite.ts index 55d04da45..0baf8ea1a 100644 --- a/packages/app/vite.ts +++ b/packages/app/vite.ts @@ -3,6 +3,8 @@ import type { PluginOption } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; +import { physical, rootRoute } from "@tanstack/virtual-file-routes"; +import { consoleRoutes } from "@executor-js/react/console-routes"; import executorVitePlugin from "@executor-js/vite-plugin"; const APP_ROOT = fileURLToPath(new URL("./", import.meta.url)); @@ -58,6 +60,14 @@ export default function appPlugin(options: AppPluginOptions = {}): PluginOption[ autoCodeSplitting: true, routesDirectory: fileURLToPath(new URL("./src/routes", import.meta.url)), generatedRouteTree: fileURLToPath(new URL("./src/routeTree.gen.ts", import.meta.url)), + // Shared console routes come from @executor-js/react (see its + // console-routes.ts); this app owns its root and the local-specific + // routes under src/routes/app. /secrets is excluded: the local app's + // variant shows credential-provider info. + virtualRouteConfig: rootRoute("__root.tsx", [ + ...consoleRoutes({ dir: "../../../react/src/routes", exclude: ["/secrets"] }), + physical("", "app"), + ]), }), ...react(), ]; diff --git a/packages/react/package.json b/packages/react/package.json index 6deace349..9ccba4b1f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,6 +23,7 @@ }, "scripts": { "test": "vitest run", + "routes:gen": "tsr generate", "test:watch": "vitest", "typecheck": "tsgo --noEmit", "typecheck:slow": "tsc --noEmit", diff --git a/packages/react/src/console-routes.test.ts b/packages/react/src/console-routes.test.ts new file mode 100644 index 000000000..0ccb66b60 --- /dev/null +++ b/packages/react/src/console-routes.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "@effect/vitest"; + +import { CONSOLE_ROUTE_PATHS, consoleRoutes } from "./console-routes"; +import { routeTree } from "./routes/routeTree.gen"; + +// consoleRoutes() (what apps mount) and routes/routeTree.gen.ts (what `bunx +// tsr generate` scans from src/routes, and what types this package's links) +// are two views of the same directory. If they drift — a route file added +// without a consoleRoutes() entry, or vice versa — apps would silently lack a +// route that this package's pages link to. Lock them together. + +const collectPaths = (route: unknown): ReadonlyArray => { + const node = route as { + options?: { id?: string }; + children?: ReadonlyArray; + }; + const children = node.children ?? []; + const id = node.options?.id; + const own = typeof id === "string" ? [id] : []; + return [...own, ...children.flatMap(collectPaths)]; +}; + +describe("console route contract", () => { + it("consoleRoutes() declares exactly the routes in src/routes", () => { + const generated = new Set(collectPaths(routeTree)); + expect([...generated].sort()).toEqual([...CONSOLE_ROUTE_PATHS].sort()); + }); + + it("every path has a virtual route node and exclude removes it", () => { + const all = consoleRoutes({ dir: "shared" }); + expect(all).toHaveLength(CONSOLE_ROUTE_PATHS.length); + const withoutSecrets = consoleRoutes({ dir: "shared", exclude: ["/secrets"] }); + expect(withoutSecrets).toHaveLength(CONSOLE_ROUTE_PATHS.length - 1); + expect(JSON.stringify(withoutSecrets)).not.toContain("secrets.tsx"); + }); +});