@agent-native/dispatch 0.13.11 → 0.13.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-control-plane.d.ts","sourceRoot":"","sources":["../../src/components/dispatch-control-plane.tsx"],"names":[],"mappings":"AAkNA,wBAAgB,oBAAoB,oDAqBnC"}
1
+ {"version":3,"file":"dispatch-control-plane.d.ts","sourceRoot":"","sources":["../../src/components/dispatch-control-plane.tsx"],"names":[],"mappings":"AAkNA,wBAAgB,oBAAoB,oDAiBnC"}
@@ -57,7 +57,7 @@ function AppsPanel({ apps, isLoading, }) {
57
57
  return (_jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeader, { icon: IconStack3, title: "Apps", detail: visibleApps.length === 1 ? "1 active" : `${visibleApps.length} active`, action: _jsx(Button, { variant: "outline", size: "sm", asChild: true, children: _jsxs(Link, { to: "/apps", children: ["View all", _jsx(IconArrowUpRight, { size: 14 })] }) }) }), showSkeletons ? (_jsx("div", { className: "grid gap-3 md:grid-cols-2", children: Array.from({ length: 4 }).map((_, index) => (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx(Skeleton, { className: "h-4 w-32" }), _jsx(Skeleton, { className: "mt-3 h-3 w-24" }), _jsx(Skeleton, { className: "mt-3 h-3 w-full" })] }, index))) })) : visibleApps.length > 0 ? (_jsx("div", { className: "grid gap-3 md:grid-cols-2", children: visibleApps.map((app) => (_jsx(WorkspaceAppCard, { app: app, className: "min-h-32" }, app.id))) })) : (_jsx(CreateAppPopover, {}))] }));
58
58
  }
59
59
  export function DispatchControlPlane() {
60
- const { data: workspaceApps = [], isLoading: appsLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false, includeArchived: true }, { refetchInterval: 2_000 });
60
+ const { data: workspaceApps = [], isLoading: appsLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false, includeArchived: true });
61
61
  return (_jsx(DispatchShell, { title: "Overview", description: "Ask Dispatch or jump into a workspace app.", children: _jsxs("div", { className: "flex flex-col gap-6", children: [_jsx(CommandPanel, {}), _jsx(WorkspaceLinks, {}), _jsx(AppsPanel, { apps: workspaceApps ?? [], isLoading: appsLoading })] }) }));
62
62
  }
63
63
  //# sourceMappingURL=dispatch-control-plane.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-control-plane.js","sourceRoot":"","sources":["../../src/components/dispatch-control-plane.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n PromptComposer,\n isInBuilderFrame,\n useActionQuery,\n useChatModels,\n useT,\n} from \"@agent-native/core/client\";\nimport {\n IconArrowUpRight,\n IconBroadcast,\n IconStack3,\n type IconProps,\n} from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\n\nimport { submitOverviewPrompt } from \"../lib/overview-chat\";\nimport type { WorkspaceAppSummary } from \"../lib/workspace-apps\";\nimport { CreateAppPopover } from \"./create-app-popover\";\nimport { DispatchShell } from \"./dispatch-shell\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\nimport { WorkspaceAppCard } from \"./workspace-app-card\";\n\nconst PROMPT_SUGGESTIONS = [\n \"Summarize the current workspace health\",\n \"Create an app for onboarding requests\",\n \"Check which agents can help with analytics\",\n];\n\nconst WORKSPACE_LINKS = [\n { to: \"/automations\", labelKey: \"dispatch.nav.automations\" },\n { to: \"/approvals\", labelKey: \"dispatch.nav.approvals\" },\n { to: \"/destinations\", labelKey: \"dispatch.nav.delivery\" },\n { to: \"/agents\", labelKey: \"dispatch.nav.agents\" },\n { to: \"/vault\", labelKey: \"dispatch.nav.vault\" },\n { to: \"/audit\", labelKey: \"dispatch.nav.audit\" },\n] as const;\n\nfunction SectionHeader({\n icon: Icon,\n title,\n detail,\n action,\n}: {\n icon: React.ComponentType<IconProps>;\n title: string;\n detail?: string;\n action?: ReactNode;\n}) {\n return (\n <div className=\"flex min-w-0 items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Icon size={16} className=\"shrink-0 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {title}\n </h2>\n {detail ? (\n <p className=\"mt-0.5 truncate text-xs text-muted-foreground\">\n {detail}\n </p>\n ) : null}\n </div>\n </div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n </div>\n );\n}\n\nfunction CommandPanel() {\n const { selectedModel } = useChatModels();\n const navigate = useNavigate();\n\n function send(message: string) {\n const trimmed = message.trim();\n if (!trimmed) return;\n\n if (isInBuilderFrame()) {\n submitOverviewPrompt(trimmed, selectedModel);\n return;\n }\n\n navigate(\"/chat\", {\n state: {\n dispatchPrompt: {\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n message: trimmed,\n selectedModel,\n },\n },\n });\n }\n\n return (\n <section className=\"rounded-lg border bg-card p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <IconBroadcast size={16} className=\"text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold text-foreground\">\n Ask Dispatch\n </h2>\n </div>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to=\"/chat\">\n Open chat\n <IconArrowUpRight size={14} />\n </Link>\n </Button>\n </div>\n <PromptComposer\n placeholder=\"Route work, inspect status, or create an app...\"\n onSubmit={(text) => send(text)}\n />\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {PROMPT_SUGGESTIONS.map((suggestion) => (\n <button\n key={suggestion}\n type=\"button\"\n onClick={() => send(suggestion)}\n className=\"cursor-pointer rounded-md border border-border bg-background px-2.5 py-1.5 text-xs text-muted-foreground transition hover:border-foreground/30 hover:text-foreground\"\n >\n {suggestion}\n </button>\n ))}\n </div>\n </section>\n );\n}\n\nfunction WorkspaceLinks() {\n const t = useT();\n\n return (\n <nav\n aria-label={t(\"dispatch.pages.workspaceShortcutsAria\")}\n className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground\"\n >\n <span className=\"font-medium text-foreground/70\">\n {t(\"dispatch.pages.also\")}\n </span>\n {WORKSPACE_LINKS.map((item, index) => (\n <span key={item.to} className=\"inline-flex items-center gap-x-3\">\n {index > 0 ? (\n <span aria-hidden className=\"text-border\">\n ·\n </span>\n ) : null}\n <Link\n to={item.to}\n className=\"transition hover:text-foreground hover:underline underline-offset-4\"\n >\n {t(item.labelKey)}\n </Link>\n </span>\n ))}\n </nav>\n );\n}\n\nfunction AppsPanel({\n apps,\n isLoading,\n}: {\n apps: WorkspaceAppSummary[];\n isLoading: boolean;\n}) {\n const visibleApps = apps.filter((app) => !app.isDispatch && !app.archived);\n const showSkeletons = isLoading && visibleApps.length === 0;\n\n return (\n <section className=\"flex flex-col gap-3\">\n <SectionHeader\n icon={IconStack3}\n title=\"Apps\"\n detail={\n visibleApps.length === 1 ? \"1 active\" : `${visibleApps.length} active`\n }\n action={\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to=\"/apps\">\n View all\n <IconArrowUpRight size={14} />\n </Link>\n </Button>\n }\n />\n {showSkeletons ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n {Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"mt-3 h-3 w-24\" />\n <Skeleton className=\"mt-3 h-3 w-full\" />\n </div>\n ))}\n </div>\n ) : visibleApps.length > 0 ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n {visibleApps.map((app) => (\n <WorkspaceAppCard key={app.id} app={app} className=\"min-h-32\" />\n ))}\n </div>\n ) : (\n <CreateAppPopover />\n )}\n </section>\n );\n}\n\nexport function DispatchControlPlane() {\n const { data: workspaceApps = [], isLoading: appsLoading } = useActionQuery<\n WorkspaceAppSummary[]\n >(\n \"list-workspace-apps\",\n { includeAgentCards: false, includeArchived: true },\n { refetchInterval: 2_000 },\n );\n\n return (\n <DispatchShell\n title=\"Overview\"\n description=\"Ask Dispatch or jump into a workspace app.\"\n >\n <div className=\"flex flex-col gap-6\">\n <CommandPanel />\n <WorkspaceLinks />\n <AppsPanel apps={workspaceApps ?? []} isLoading={appsLoading} />\n </div>\n </DispatchShell>\n );\n}\n"]}
1
+ {"version":3,"file":"dispatch-control-plane.js","sourceRoot":"","sources":["../../src/components/dispatch-control-plane.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n PromptComposer,\n isInBuilderFrame,\n useActionQuery,\n useChatModels,\n useT,\n} from \"@agent-native/core/client\";\nimport {\n IconArrowUpRight,\n IconBroadcast,\n IconStack3,\n type IconProps,\n} from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\n\nimport { submitOverviewPrompt } from \"../lib/overview-chat\";\nimport type { WorkspaceAppSummary } from \"../lib/workspace-apps\";\nimport { CreateAppPopover } from \"./create-app-popover\";\nimport { DispatchShell } from \"./dispatch-shell\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\nimport { WorkspaceAppCard } from \"./workspace-app-card\";\n\nconst PROMPT_SUGGESTIONS = [\n \"Summarize the current workspace health\",\n \"Create an app for onboarding requests\",\n \"Check which agents can help with analytics\",\n];\n\nconst WORKSPACE_LINKS = [\n { to: \"/automations\", labelKey: \"dispatch.nav.automations\" },\n { to: \"/approvals\", labelKey: \"dispatch.nav.approvals\" },\n { to: \"/destinations\", labelKey: \"dispatch.nav.delivery\" },\n { to: \"/agents\", labelKey: \"dispatch.nav.agents\" },\n { to: \"/vault\", labelKey: \"dispatch.nav.vault\" },\n { to: \"/audit\", labelKey: \"dispatch.nav.audit\" },\n] as const;\n\nfunction SectionHeader({\n icon: Icon,\n title,\n detail,\n action,\n}: {\n icon: React.ComponentType<IconProps>;\n title: string;\n detail?: string;\n action?: ReactNode;\n}) {\n return (\n <div className=\"flex min-w-0 items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Icon size={16} className=\"shrink-0 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {title}\n </h2>\n {detail ? (\n <p className=\"mt-0.5 truncate text-xs text-muted-foreground\">\n {detail}\n </p>\n ) : null}\n </div>\n </div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n </div>\n );\n}\n\nfunction CommandPanel() {\n const { selectedModel } = useChatModels();\n const navigate = useNavigate();\n\n function send(message: string) {\n const trimmed = message.trim();\n if (!trimmed) return;\n\n if (isInBuilderFrame()) {\n submitOverviewPrompt(trimmed, selectedModel);\n return;\n }\n\n navigate(\"/chat\", {\n state: {\n dispatchPrompt: {\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n message: trimmed,\n selectedModel,\n },\n },\n });\n }\n\n return (\n <section className=\"rounded-lg border bg-card p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <IconBroadcast size={16} className=\"text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold text-foreground\">\n Ask Dispatch\n </h2>\n </div>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to=\"/chat\">\n Open chat\n <IconArrowUpRight size={14} />\n </Link>\n </Button>\n </div>\n <PromptComposer\n placeholder=\"Route work, inspect status, or create an app...\"\n onSubmit={(text) => send(text)}\n />\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {PROMPT_SUGGESTIONS.map((suggestion) => (\n <button\n key={suggestion}\n type=\"button\"\n onClick={() => send(suggestion)}\n className=\"cursor-pointer rounded-md border border-border bg-background px-2.5 py-1.5 text-xs text-muted-foreground transition hover:border-foreground/30 hover:text-foreground\"\n >\n {suggestion}\n </button>\n ))}\n </div>\n </section>\n );\n}\n\nfunction WorkspaceLinks() {\n const t = useT();\n\n return (\n <nav\n aria-label={t(\"dispatch.pages.workspaceShortcutsAria\")}\n className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground\"\n >\n <span className=\"font-medium text-foreground/70\">\n {t(\"dispatch.pages.also\")}\n </span>\n {WORKSPACE_LINKS.map((item, index) => (\n <span key={item.to} className=\"inline-flex items-center gap-x-3\">\n {index > 0 ? (\n <span aria-hidden className=\"text-border\">\n ·\n </span>\n ) : null}\n <Link\n to={item.to}\n className=\"transition hover:text-foreground hover:underline underline-offset-4\"\n >\n {t(item.labelKey)}\n </Link>\n </span>\n ))}\n </nav>\n );\n}\n\nfunction AppsPanel({\n apps,\n isLoading,\n}: {\n apps: WorkspaceAppSummary[];\n isLoading: boolean;\n}) {\n const visibleApps = apps.filter((app) => !app.isDispatch && !app.archived);\n const showSkeletons = isLoading && visibleApps.length === 0;\n\n return (\n <section className=\"flex flex-col gap-3\">\n <SectionHeader\n icon={IconStack3}\n title=\"Apps\"\n detail={\n visibleApps.length === 1 ? \"1 active\" : `${visibleApps.length} active`\n }\n action={\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to=\"/apps\">\n View all\n <IconArrowUpRight size={14} />\n </Link>\n </Button>\n }\n />\n {showSkeletons ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n {Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"mt-3 h-3 w-24\" />\n <Skeleton className=\"mt-3 h-3 w-full\" />\n </div>\n ))}\n </div>\n ) : visibleApps.length > 0 ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n {visibleApps.map((app) => (\n <WorkspaceAppCard key={app.id} app={app} className=\"min-h-32\" />\n ))}\n </div>\n ) : (\n <CreateAppPopover />\n )}\n </section>\n );\n}\n\nexport function DispatchControlPlane() {\n const { data: workspaceApps = [], isLoading: appsLoading } = useActionQuery<\n WorkspaceAppSummary[]\n >(\"list-workspace-apps\", { includeAgentCards: false, includeArchived: true });\n\n return (\n <DispatchShell\n title=\"Overview\"\n description=\"Ask Dispatch or jump into a workspace app.\"\n >\n <div className=\"flex flex-col gap-6\">\n <CommandPanel />\n <WorkspaceLinks />\n <AppsPanel apps={workspaceApps ?? []} isLoading={appsLoading} />\n </div>\n </DispatchShell>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-navigation-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-navigation-state.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,uBAAuB,EAExB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,uBAAuB,QAmEtE;AAqBD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,SAAK,EACX,UAAU,CAAC,EAAE,uBAAuB,GACnC,eAAe,CA6BjB"}
1
+ {"version":3,"file":"use-navigation-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-navigation-state.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,uBAAuB,EAExB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,uBAAuB,QAkEtE;AAqBD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,SAAK,EACX,UAAU,CAAC,EAAE,uBAAuB,GACnC,eAAe,CA6BjB"}
@@ -32,7 +32,6 @@ export function useNavigationState(extensions) {
32
32
  }
33
33
  return null;
34
34
  },
35
- refetchInterval: 2_000,
36
35
  structuralSharing: false,
37
36
  });
38
37
  useEffect(() => {
@@ -235,7 +234,7 @@ function resolvePath(view, extensions, command) {
235
234
  case "threads":
236
235
  return "/thread-debug";
237
236
  case "team":
238
- return "/settings#team";
237
+ return "/settings#organization";
239
238
  case "extensions":
240
239
  return command?.extensionId
241
240
  ? `/extensions/${encodeURIComponent(command.extensionId)}`
@@ -1 +1 @@
1
- {"version":3,"file":"use-navigation-state.js","sourceRoot":"","sources":["../../src/hooks/use-navigation-state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n agentNativePath,\n appBasePath,\n appPath,\n markAgentChatHomeHandoff,\n} from \"@agent-native/core/client\";\nimport { extensionIdFromPathname } from \"@agent-native/core/client/extensions\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useEffect } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport type {\n DispatchExtensionConfig,\n DispatchNavItem,\n} from \"../components/index.js\";\n\nexport interface NavigationState {\n view: string;\n path?: string;\n extensionId?: string;\n extensionSlug?: string;\n dreamId?: string;\n sourceId?: string;\n query?: string;\n threadId?: string;\n}\n\nexport function useNavigationState(extensions?: DispatchExtensionConfig) {\n const location = useLocation();\n const navigate = useNavigate();\n const qc = useQueryClient();\n\n // Sync current route to application state\n useEffect(() => {\n const localPathname = routerPath(location.pathname);\n const state = buildDispatchNavigationState(\n localPathname,\n location.search,\n extensions,\n );\n\n fetch(agentNativePath(\"/_agent-native/application-state/navigation\"), {\n method: \"PUT\",\n keepalive: true,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(state),\n }).catch(() => {});\n }, [extensions, location.pathname, location.search]);\n\n // Listen for navigate commands from agent\n const { data: navCommand } = useQuery({\n queryKey: [\"navigate-command\"],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\"/_agent-native/application-state/navigate\"),\n );\n if (!res.ok) return null;\n const data = await res.json();\n if (data) {\n // Return with a timestamp to ensure uniqueness\n return { ...data, _ts: Date.now() };\n }\n return null;\n },\n refetchInterval: 2_000,\n structuralSharing: false,\n });\n\n useEffect(() => {\n if (!navCommand) return;\n // Delete the one-shot command AFTER reading it\n fetch(agentNativePath(\"/_agent-native/application-state/navigate\"), {\n method: \"DELETE\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n }).catch(() => {});\n const cmd = navCommand as NavigationState;\n\n // Navigate to a specific path or resolve view name to path\n const resolvedPath =\n cmd.path || resolvePath(cmd.view, extensions, cmd) || \"/overview\";\n const path =\n cmd.view === \"dreams\" && cmd.dreamId && !resolvedPath.includes(\"?\")\n ? `${resolvedPath}?dreamId=${encodeURIComponent(cmd.dreamId)}`\n : resolvedPath;\n const nextPath = routerPath(path);\n if (\n isChatPath(routerPath(location.pathname)) &&\n !isChatPath(pathnameFromPath(nextPath))\n ) {\n markAgentChatHomeHandoff(\"dispatch\");\n }\n navigate(nextPath);\n qc.setQueryData([\"navigate-command\"], null);\n }, [extensions, location.pathname, navCommand, navigate, qc]);\n}\n\nfunction pathnameFromPath(path: string): string {\n return path.split(/[?#]/, 1)[0] || \"/\";\n}\n\nfunction isChatPath(pathname: string): boolean {\n return pathname === \"/chat\" || pathname.startsWith(\"/chat/\");\n}\n\nfunction threadIdFromPath(pathname: string): string | null {\n const match = pathname.match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\nexport function buildDispatchNavigationState(\n pathname: string,\n search = \"\",\n extensions?: DispatchExtensionConfig,\n): NavigationState {\n const state: NavigationState = {\n view: resolveView(pathname, extensions),\n path: appPath(pathname),\n };\n\n const threadId = threadIdFromPath(pathname);\n if (threadId) state.threadId = threadId;\n\n const extensionId = extensionIdFromPathname(pathname);\n if (extensionId) {\n state.view = \"extensions\";\n state.extensionId = extensionId;\n const slug = extensionSlugFromPathname(pathname);\n if (slug) state.extensionSlug = slug;\n return state;\n }\n\n if (state.view === \"dreams\") {\n const params = new URLSearchParams(search);\n const dreamId = params.get(\"dreamId\");\n const sourceId = params.get(\"sourceId\");\n const query = params.get(\"query\");\n if (dreamId) state.dreamId = dreamId;\n if (sourceId) state.sourceId = sourceId;\n if (query) state.query = query;\n }\n\n return state;\n}\n\nfunction routerPath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n let result = path;\n // Iteratively strip basename. A path that arrives doubly-prefixed\n // (e.g. \"/dispatch/dispatch/overview\", possibly from a stale link or a\n // prior bug) would otherwise get partially stripped here and then\n // re-prefixed by react-router's basename, restoring the bad URL.\n for (let i = 0; i < 4; i += 1) {\n if (result === basePath) return \"/\";\n if (!result.startsWith(`${basePath}/`)) break;\n result = result.slice(basePath.length) || \"/\";\n }\n return result;\n}\n\nfunction extensionItemMatchesPath(\n item: DispatchNavItem,\n pathname: string,\n): boolean {\n if (item.match) {\n try {\n if (item.match(pathname)) return true;\n } catch {\n return false;\n }\n }\n return pathname === item.to || pathname.startsWith(`${item.to}/`);\n}\n\nfunction resolveExtensionView(\n pathname: string,\n extensions?: DispatchExtensionConfig,\n): string | undefined {\n return extensions?.navItems?.find((item) =>\n extensionItemMatchesPath(item, pathname),\n )?.id;\n}\n\nfunction resolveExtensionPath(\n view: string | undefined,\n extensions?: DispatchExtensionConfig,\n): string | undefined {\n if (!view) return undefined;\n return extensions?.navItems?.find((item) => item.id === view)?.to;\n}\n\nfunction resolveView(\n pathname: string,\n extensions?: DispatchExtensionConfig,\n): string {\n const extensionView = resolveExtensionView(pathname, extensions);\n if (extensionView) return extensionView;\n if (pathname === \"/extensions\" || pathname.startsWith(\"/extensions/\")) {\n return \"extensions\";\n }\n if (pathname.startsWith(\"/chat\")) return \"chat\";\n if (pathname.startsWith(\"/apps\")) return \"apps\";\n if (pathname.startsWith(\"/metrics\")) return \"metrics\";\n if (pathname.startsWith(\"/new-app\")) return \"new-app\";\n if (pathname.startsWith(\"/vault\")) return \"vault\";\n if (pathname.startsWith(\"/integrations\")) return \"integrations\";\n if (pathname.startsWith(\"/workspace\")) return \"workspace\";\n if (pathname.startsWith(\"/agents\")) return \"agents\";\n if (pathname.startsWith(\"/messaging\")) return \"messaging\";\n if (pathname.startsWith(\"/destinations\")) return \"destinations\";\n if (pathname.startsWith(\"/identities\")) return \"identities\";\n if (pathname.startsWith(\"/approvals\")) return \"approvals\";\n if (pathname.startsWith(\"/automations\")) return \"automations\";\n if (pathname.startsWith(\"/audit\")) return \"audit\";\n if (pathname.startsWith(\"/dreams\")) return \"dreams\";\n if (pathname.startsWith(\"/thread-debug\")) return \"thread-debug\";\n if (pathname.startsWith(\"/team\")) return \"settings\";\n return \"overview\";\n}\n\nfunction resolvePath(\n view?: string,\n extensions?: DispatchExtensionConfig,\n command?: Pick<NavigationState, \"extensionId\" | \"threadId\">,\n): string | undefined {\n switch (view) {\n case \"chat\":\n case \"ask\":\n return command?.threadId && command.threadId.trim()\n ? `/chat/${encodeURIComponent(command.threadId.trim())}`\n : \"/chat\";\n case \"overview\":\n return \"/overview\";\n case \"apps\":\n return \"/apps\";\n case \"metrics\":\n case \"usage\":\n return \"/metrics\";\n case \"new-app\":\n case \"create-app\":\n return \"/new-app\";\n case \"vault\":\n case \"secrets\":\n return \"/vault\";\n case \"integrations\":\n return \"/integrations\";\n case \"workspace\":\n case \"resources\":\n return \"/workspace\";\n case \"agents\":\n return \"/agents\";\n case \"messaging\":\n return \"/messaging\";\n case \"destinations\":\n case \"routes\":\n return \"/destinations\";\n case \"identities\":\n return \"/identities\";\n case \"approvals\":\n return \"/approvals\";\n case \"automations\":\n case \"jobs\":\n return \"/automations\";\n case \"audit\":\n return \"/audit\";\n case \"dreams\":\n return \"/dreams\";\n case \"thread-debug\":\n case \"threads\":\n return \"/thread-debug\";\n case \"team\":\n return \"/settings#team\";\n case \"extensions\":\n return command?.extensionId\n ? `/extensions/${encodeURIComponent(command.extensionId)}`\n : \"/extensions\";\n default:\n return resolveExtensionPath(view, extensions);\n }\n}\n\nfunction extensionSlugFromPathname(pathname: string): string | undefined {\n const match = pathname.match(/^\\/extensions\\/[^/]+\\/([^/?#]+)/);\n if (!match?.[1]) return undefined;\n try {\n return decodeURIComponent(match[1]);\n } catch {\n return match[1];\n }\n}\n"]}
1
+ {"version":3,"file":"use-navigation-state.js","sourceRoot":"","sources":["../../src/hooks/use-navigation-state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n agentNativePath,\n appBasePath,\n appPath,\n markAgentChatHomeHandoff,\n} from \"@agent-native/core/client\";\nimport { extensionIdFromPathname } from \"@agent-native/core/client/extensions\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useEffect } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport type {\n DispatchExtensionConfig,\n DispatchNavItem,\n} from \"../components/index.js\";\n\nexport interface NavigationState {\n view: string;\n path?: string;\n extensionId?: string;\n extensionSlug?: string;\n dreamId?: string;\n sourceId?: string;\n query?: string;\n threadId?: string;\n}\n\nexport function useNavigationState(extensions?: DispatchExtensionConfig) {\n const location = useLocation();\n const navigate = useNavigate();\n const qc = useQueryClient();\n\n // Sync current route to application state\n useEffect(() => {\n const localPathname = routerPath(location.pathname);\n const state = buildDispatchNavigationState(\n localPathname,\n location.search,\n extensions,\n );\n\n fetch(agentNativePath(\"/_agent-native/application-state/navigation\"), {\n method: \"PUT\",\n keepalive: true,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(state),\n }).catch(() => {});\n }, [extensions, location.pathname, location.search]);\n\n // Listen for navigate commands from agent\n const { data: navCommand } = useQuery({\n queryKey: [\"navigate-command\"],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\"/_agent-native/application-state/navigate\"),\n );\n if (!res.ok) return null;\n const data = await res.json();\n if (data) {\n // Return with a timestamp to ensure uniqueness\n return { ...data, _ts: Date.now() };\n }\n return null;\n },\n structuralSharing: false,\n });\n\n useEffect(() => {\n if (!navCommand) return;\n // Delete the one-shot command AFTER reading it\n fetch(agentNativePath(\"/_agent-native/application-state/navigate\"), {\n method: \"DELETE\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n }).catch(() => {});\n const cmd = navCommand as NavigationState;\n\n // Navigate to a specific path or resolve view name to path\n const resolvedPath =\n cmd.path || resolvePath(cmd.view, extensions, cmd) || \"/overview\";\n const path =\n cmd.view === \"dreams\" && cmd.dreamId && !resolvedPath.includes(\"?\")\n ? `${resolvedPath}?dreamId=${encodeURIComponent(cmd.dreamId)}`\n : resolvedPath;\n const nextPath = routerPath(path);\n if (\n isChatPath(routerPath(location.pathname)) &&\n !isChatPath(pathnameFromPath(nextPath))\n ) {\n markAgentChatHomeHandoff(\"dispatch\");\n }\n navigate(nextPath);\n qc.setQueryData([\"navigate-command\"], null);\n }, [extensions, location.pathname, navCommand, navigate, qc]);\n}\n\nfunction pathnameFromPath(path: string): string {\n return path.split(/[?#]/, 1)[0] || \"/\";\n}\n\nfunction isChatPath(pathname: string): boolean {\n return pathname === \"/chat\" || pathname.startsWith(\"/chat/\");\n}\n\nfunction threadIdFromPath(pathname: string): string | null {\n const match = pathname.match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\nexport function buildDispatchNavigationState(\n pathname: string,\n search = \"\",\n extensions?: DispatchExtensionConfig,\n): NavigationState {\n const state: NavigationState = {\n view: resolveView(pathname, extensions),\n path: appPath(pathname),\n };\n\n const threadId = threadIdFromPath(pathname);\n if (threadId) state.threadId = threadId;\n\n const extensionId = extensionIdFromPathname(pathname);\n if (extensionId) {\n state.view = \"extensions\";\n state.extensionId = extensionId;\n const slug = extensionSlugFromPathname(pathname);\n if (slug) state.extensionSlug = slug;\n return state;\n }\n\n if (state.view === \"dreams\") {\n const params = new URLSearchParams(search);\n const dreamId = params.get(\"dreamId\");\n const sourceId = params.get(\"sourceId\");\n const query = params.get(\"query\");\n if (dreamId) state.dreamId = dreamId;\n if (sourceId) state.sourceId = sourceId;\n if (query) state.query = query;\n }\n\n return state;\n}\n\nfunction routerPath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n let result = path;\n // Iteratively strip basename. A path that arrives doubly-prefixed\n // (e.g. \"/dispatch/dispatch/overview\", possibly from a stale link or a\n // prior bug) would otherwise get partially stripped here and then\n // re-prefixed by react-router's basename, restoring the bad URL.\n for (let i = 0; i < 4; i += 1) {\n if (result === basePath) return \"/\";\n if (!result.startsWith(`${basePath}/`)) break;\n result = result.slice(basePath.length) || \"/\";\n }\n return result;\n}\n\nfunction extensionItemMatchesPath(\n item: DispatchNavItem,\n pathname: string,\n): boolean {\n if (item.match) {\n try {\n if (item.match(pathname)) return true;\n } catch {\n return false;\n }\n }\n return pathname === item.to || pathname.startsWith(`${item.to}/`);\n}\n\nfunction resolveExtensionView(\n pathname: string,\n extensions?: DispatchExtensionConfig,\n): string | undefined {\n return extensions?.navItems?.find((item) =>\n extensionItemMatchesPath(item, pathname),\n )?.id;\n}\n\nfunction resolveExtensionPath(\n view: string | undefined,\n extensions?: DispatchExtensionConfig,\n): string | undefined {\n if (!view) return undefined;\n return extensions?.navItems?.find((item) => item.id === view)?.to;\n}\n\nfunction resolveView(\n pathname: string,\n extensions?: DispatchExtensionConfig,\n): string {\n const extensionView = resolveExtensionView(pathname, extensions);\n if (extensionView) return extensionView;\n if (pathname === \"/extensions\" || pathname.startsWith(\"/extensions/\")) {\n return \"extensions\";\n }\n if (pathname.startsWith(\"/chat\")) return \"chat\";\n if (pathname.startsWith(\"/apps\")) return \"apps\";\n if (pathname.startsWith(\"/metrics\")) return \"metrics\";\n if (pathname.startsWith(\"/new-app\")) return \"new-app\";\n if (pathname.startsWith(\"/vault\")) return \"vault\";\n if (pathname.startsWith(\"/integrations\")) return \"integrations\";\n if (pathname.startsWith(\"/workspace\")) return \"workspace\";\n if (pathname.startsWith(\"/agents\")) return \"agents\";\n if (pathname.startsWith(\"/messaging\")) return \"messaging\";\n if (pathname.startsWith(\"/destinations\")) return \"destinations\";\n if (pathname.startsWith(\"/identities\")) return \"identities\";\n if (pathname.startsWith(\"/approvals\")) return \"approvals\";\n if (pathname.startsWith(\"/automations\")) return \"automations\";\n if (pathname.startsWith(\"/audit\")) return \"audit\";\n if (pathname.startsWith(\"/dreams\")) return \"dreams\";\n if (pathname.startsWith(\"/thread-debug\")) return \"thread-debug\";\n if (pathname.startsWith(\"/team\")) return \"settings\";\n return \"overview\";\n}\n\nfunction resolvePath(\n view?: string,\n extensions?: DispatchExtensionConfig,\n command?: Pick<NavigationState, \"extensionId\" | \"threadId\">,\n): string | undefined {\n switch (view) {\n case \"chat\":\n case \"ask\":\n return command?.threadId && command.threadId.trim()\n ? `/chat/${encodeURIComponent(command.threadId.trim())}`\n : \"/chat\";\n case \"overview\":\n return \"/overview\";\n case \"apps\":\n return \"/apps\";\n case \"metrics\":\n case \"usage\":\n return \"/metrics\";\n case \"new-app\":\n case \"create-app\":\n return \"/new-app\";\n case \"vault\":\n case \"secrets\":\n return \"/vault\";\n case \"integrations\":\n return \"/integrations\";\n case \"workspace\":\n case \"resources\":\n return \"/workspace\";\n case \"agents\":\n return \"/agents\";\n case \"messaging\":\n return \"/messaging\";\n case \"destinations\":\n case \"routes\":\n return \"/destinations\";\n case \"identities\":\n return \"/identities\";\n case \"approvals\":\n return \"/approvals\";\n case \"automations\":\n case \"jobs\":\n return \"/automations\";\n case \"audit\":\n return \"/audit\";\n case \"dreams\":\n return \"/dreams\";\n case \"thread-debug\":\n case \"threads\":\n return \"/thread-debug\";\n case \"team\":\n return \"/settings#organization\";\n case \"extensions\":\n return command?.extensionId\n ? `/extensions/${encodeURIComponent(command.extensionId)}`\n : \"/extensions\";\n default:\n return resolveExtensionPath(view, extensions);\n }\n}\n\nfunction extensionSlugFromPathname(pathname: string): string | undefined {\n const match = pathname.match(/^\\/extensions\\/[^/]+\\/([^/?#]+)/);\n if (!match?.[1]) return undefined;\n try {\n return decodeURIComponent(match[1]);\n } catch {\n return match[1];\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAYtB,wBAAgB,IAAI;;IAEnB;AA2CD,wBAAsB,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,iBAQ1D;AAED,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,YAAY,GACb,EAAE,wBAAwB,oBAS1B;AAED,MAAM,CAAC,OAAO,UAAU,yBAAyB,oDAmGhD"}
1
+ {"version":3,"file":"$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAYtB,wBAAgB,IAAI;;IAEnB;AA2CD,wBAAsB,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,iBAQ1D;AAED,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,YAAY,GACb,EAAE,wBAAwB,oBAS1B;AAED,MAAM,CAAC,OAAO,UAAU,yBAAyB,oDAiGhD"}
@@ -79,7 +79,9 @@ export async function clientLoader({ params, serverLoader, }) {
79
79
  export default function WorkspaceAppCatchAllRoute() {
80
80
  const t = useT();
81
81
  const { appId } = useParams();
82
- const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false }, { refetchInterval: 2_000 });
82
+ const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", {
83
+ includeAgentCards: false,
84
+ });
83
85
  const app = useMemo(() => apps.find((item) => item.id === appId) ?? null, [appId, apps]);
84
86
  const href = app ? workspaceAppHref(app) : null;
85
87
  const isSelfReference = appId === "dispatch";
@@ -1 +1 @@
1
- {"version":3,"file":"$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery, appPath, useT } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useEffect, useMemo } from \"react\";\nimport {\n Link,\n Navigate,\n redirect,\n useParams,\n type ClientLoaderFunctionArgs,\n type LoaderFunctionArgs,\n} from \"react-router\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Spinner } from \"../../components/ui/spinner\";\nimport { resolveServerCatchAllTarget } from \"../../lib/catch-all-target\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\n/**\n * Catch-all for `/dispatch/<segment>` paths that don't match an explicit\n * Dispatch route. When `<segment>` is the id of a workspace app sibling\n * (e.g. `/dispatch/todo` after Builder.io routes a \"navigate to /todo\"\n * call through Dispatch's mount point), bounce to the absolute `/<appId>`\n * so the user lands on the actual app instead of a 404 inside Dispatch.\n *\n * Server-side redirect: we resolve the workspace app manifest via the\n * shared `loadWorkspaceAppsManifest()` helper, which checks the\n * `AGENT_NATIVE_WORKSPACE_APPS_JSON` env var, then the\n * `.agent-native/workspace-apps.json` file written by `workspace-deploy.ts`,\n * then a live filesystem scan of `apps/` for local dev. We then throw\n * `redirect(\"/<appId>\")`. React Router 7 does not prepend the basename to\n * absolute paths returned from a loader, so the redirect escapes Dispatch's\n * `/dispatch` mount cleanly.\n *\n * Why a catch-all instead of fixing the agent prompt: Builder.io currently\n * resolves \"navigate to /todo\" relative to Dispatch's mount, sending the\n * user to /dispatch/todo. The same wrong path then gets captured as the\n * OAuth callbackURL, so Google sign-in completes back at /dispatch/todo\n * and looks broken. This route fixes both the post-creation navigation\n * and the OAuth round-trip from a single place.\n *\n * Built-in template fallback: when no workspace manifest is available\n * (framework dev with each template on its own port, hosted dispatch with\n * no sibling apps), redirect to the matching first-party template's deploy\n * URL — `http://localhost:<devPort>` in dev, `https://<id>.agent-native.com`\n * in production. Without this, a user visiting `/forms` on dispatch is\n * forced to sign in (auth guard) and then lands on this route's \"Page not\n * found\" pane after the post-login reload.\n *\n * `appId === \"dispatch\"` short-circuit: when the segment matches Dispatch\n * itself (e.g. `/dispatch/dispatch`), we go straight to the overview rather\n * than chaining through `/dispatch` (which polled `useActionQuery` re-fired\n * `window.location.assign` against and looped forever in production).\n */\nfunction dispatchSelfRedirect(appId: string | undefined): string | null {\n if (appId === \"dispatch\") return appPath(\"/overview\");\n return null;\n}\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n const appId = params.appId;\n if (!appId) return null;\n const selfTarget = dispatchSelfRedirect(appId);\n if (selfTarget) throw redirect(selfTarget);\n const target = await resolveServerCatchAllTarget(appId);\n if (target) throw redirect(target);\n return null;\n}\n\nexport async function clientLoader({\n params,\n serverLoader,\n}: ClientLoaderFunctionArgs) {\n const selfTarget = dispatchSelfRedirect(params.appId);\n if (selfTarget) throw redirect(selfTarget);\n // Defer to the server loader so the built-in template fallback runs on\n // SPA navigations too (e.g. clicking a `/<template-id>` link inside\n // dispatch). Without this the client side would only check the workspace\n // apps query, which never lists the static first-party templates and so\n // the user would land on the \"Page not found\" pane.\n return serverLoader();\n}\n\nexport default function WorkspaceAppCatchAllRoute() {\n const t = useT();\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n { refetchInterval: 2_000 },\n );\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n const isSelfReference = appId === \"dispatch\";\n\n useEffect(() => {\n if (isSelfReference) return;\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href, isSelfReference]);\n\n if (isSelfReference) {\n return <Navigate to={appPath(\"/overview\")} replace />;\n }\n\n if ((isLoading && !app) || (app && app.status !== \"pending\" && href)) {\n return (\n <div className=\"flex h-screen w-full items-center justify-center\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n return (\n <DispatchShell\n title={app?.name || t(\"dispatch.pages.pageNotFound\")}\n description={t(\"dispatch.pages.pageNotFoundDescription\")}\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to={appPath(\"/overview\")}>\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.overview\")}\n </Link>\n </Button>\n\n {app?.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"dispatch.pages.openBuilderBranch\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.pageNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <span className=\"font-mono text-foreground\">/{appId}</span> isn't\n {t(\"dispatch.pages.notDispatchOrWorkspaceApp\")}\n </p>\n <Button asChild>\n <Link to={appPath(\"/apps\")}>\n {t(\"dispatch.pages.browseApps\")}\n </Link>\n </Button>\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
1
+ {"version":3,"file":"$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery, appPath, useT } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useEffect, useMemo } from \"react\";\nimport {\n Link,\n Navigate,\n redirect,\n useParams,\n type ClientLoaderFunctionArgs,\n type LoaderFunctionArgs,\n} from \"react-router\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Spinner } from \"../../components/ui/spinner\";\nimport { resolveServerCatchAllTarget } from \"../../lib/catch-all-target\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\n/**\n * Catch-all for `/dispatch/<segment>` paths that don't match an explicit\n * Dispatch route. When `<segment>` is the id of a workspace app sibling\n * (e.g. `/dispatch/todo` after Builder.io routes a \"navigate to /todo\"\n * call through Dispatch's mount point), bounce to the absolute `/<appId>`\n * so the user lands on the actual app instead of a 404 inside Dispatch.\n *\n * Server-side redirect: we resolve the workspace app manifest via the\n * shared `loadWorkspaceAppsManifest()` helper, which checks the\n * `AGENT_NATIVE_WORKSPACE_APPS_JSON` env var, then the\n * `.agent-native/workspace-apps.json` file written by `workspace-deploy.ts`,\n * then a live filesystem scan of `apps/` for local dev. We then throw\n * `redirect(\"/<appId>\")`. React Router 7 does not prepend the basename to\n * absolute paths returned from a loader, so the redirect escapes Dispatch's\n * `/dispatch` mount cleanly.\n *\n * Why a catch-all instead of fixing the agent prompt: Builder.io currently\n * resolves \"navigate to /todo\" relative to Dispatch's mount, sending the\n * user to /dispatch/todo. The same wrong path then gets captured as the\n * OAuth callbackURL, so Google sign-in completes back at /dispatch/todo\n * and looks broken. This route fixes both the post-creation navigation\n * and the OAuth round-trip from a single place.\n *\n * Built-in template fallback: when no workspace manifest is available\n * (framework dev with each template on its own port, hosted dispatch with\n * no sibling apps), redirect to the matching first-party template's deploy\n * URL — `http://localhost:<devPort>` in dev, `https://<id>.agent-native.com`\n * in production. Without this, a user visiting `/forms` on dispatch is\n * forced to sign in (auth guard) and then lands on this route's \"Page not\n * found\" pane after the post-login reload.\n *\n * `appId === \"dispatch\"` short-circuit: when the segment matches Dispatch\n * itself (e.g. `/dispatch/dispatch`), we go straight to the overview rather\n * than chaining through `/dispatch` (which polled `useActionQuery` re-fired\n * `window.location.assign` against and looped forever in production).\n */\nfunction dispatchSelfRedirect(appId: string | undefined): string | null {\n if (appId === \"dispatch\") return appPath(\"/overview\");\n return null;\n}\n\nexport async function loader({ params }: LoaderFunctionArgs) {\n const appId = params.appId;\n if (!appId) return null;\n const selfTarget = dispatchSelfRedirect(appId);\n if (selfTarget) throw redirect(selfTarget);\n const target = await resolveServerCatchAllTarget(appId);\n if (target) throw redirect(target);\n return null;\n}\n\nexport async function clientLoader({\n params,\n serverLoader,\n}: ClientLoaderFunctionArgs) {\n const selfTarget = dispatchSelfRedirect(params.appId);\n if (selfTarget) throw redirect(selfTarget);\n // Defer to the server loader so the built-in template fallback runs on\n // SPA navigations too (e.g. clicking a `/<template-id>` link inside\n // dispatch). Without this the client side would only check the workspace\n // apps query, which never lists the static first-party templates and so\n // the user would land on the \"Page not found\" pane.\n return serverLoader();\n}\n\nexport default function WorkspaceAppCatchAllRoute() {\n const t = useT();\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\"list-workspace-apps\", {\n includeAgentCards: false,\n });\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n const isSelfReference = appId === \"dispatch\";\n\n useEffect(() => {\n if (isSelfReference) return;\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href, isSelfReference]);\n\n if (isSelfReference) {\n return <Navigate to={appPath(\"/overview\")} replace />;\n }\n\n if ((isLoading && !app) || (app && app.status !== \"pending\" && href)) {\n return (\n <div className=\"flex h-screen w-full items-center justify-center\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n return (\n <DispatchShell\n title={app?.name || t(\"dispatch.pages.pageNotFound\")}\n description={t(\"dispatch.pages.pageNotFoundDescription\")}\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to={appPath(\"/overview\")}>\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.overview\")}\n </Link>\n </Button>\n\n {app?.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"dispatch.pages.openBuilderBranch\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.pageNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <span className=\"font-mono text-foreground\">/{appId}</span> isn't\n {t(\"dispatch.pages.notDispatchOrWorkspaceApp\")}\n </p>\n <Button asChild>\n <Link to={appPath(\"/apps\")}>\n {t(\"dispatch.pages.browseApps\")}\n </Link>\n </Button>\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"apps.$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"AAkBA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,oDAyGxC"}
1
+ {"version":3,"file":"apps.$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"AAkBA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,oDAqGxC"}
@@ -14,8 +14,8 @@ export function meta() {
14
14
  export default function WorkspaceAppRoute() {
15
15
  const t = useT();
16
16
  const { appId } = useParams();
17
- const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false }, {
18
- refetchInterval: 2_000,
17
+ const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", {
18
+ includeAgentCards: false,
19
19
  });
20
20
  const app = useMemo(() => apps.find((item) => item.id === appId) ?? null, [appId, apps]);
21
21
  const href = app ? workspaceAppHref(app) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery, useT } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useEffect, useMemo } from \"react\";\nimport { Link, useParams } from \"react-router\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const t = useT();\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n {\n refetchInterval: 2_000,\n },\n );\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href]);\n\n return (\n <DispatchShell\n title={app?.name || t(\"dispatch.pages.workspaceAppFallback\")}\n description={t(\"dispatch.pages.workspaceAppDescription\")}\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n\n {isLoading && !app ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n ) : !app ? (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n ) : app.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"dispatch.pages.openBuilderBranch\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.openingApp\", { name: app.name })}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.redirectingTo\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>.\n </p>\n {href ? (\n <Button asChild>\n <a href={href}>\n {t(\"dispatch.pages.openApp\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
1
+ {"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery, useT } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useEffect, useMemo } from \"react\";\nimport { Link, useParams } from \"react-router\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const t = useT();\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\"list-workspace-apps\", {\n includeAgentCards: false,\n });\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href]);\n\n return (\n <DispatchShell\n title={app?.name || t(\"dispatch.pages.workspaceAppFallback\")}\n description={t(\"dispatch.pages.workspaceAppDescription\")}\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n\n {isLoading && !app ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n ) : !app ? (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n ) : app.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"dispatch.pages.openBuilderBranch\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.openingApp\", { name: app.name })}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.redirectingTo\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>.\n </p>\n {href ? (\n <Button asChild>\n <a href={href}>\n {t(\"dispatch.pages.openApp\")}\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"AAwCA,wBAAgB,IAAI;;IAEnB;AA+BD,MAAM,CAAC,OAAO,UAAU,SAAS,oDAsNhC"}
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"AAwCA,wBAAgB,IAAI;;IAEnB;AA+BD,MAAM,CAAC,OAAO,UAAU,SAAS,oDAmNhC"}
@@ -29,9 +29,7 @@ export default function AppsRoute() {
29
29
  const t = useT();
30
30
  const [showHidden, setShowHidden] = useState(false);
31
31
  const [templatesOpen, setTemplatesOpen] = useState(false);
32
- const { data: apps = [], isLoading: appsLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false, includeArchived: true }, {
33
- refetchInterval: 2_000,
34
- });
32
+ const { data: apps = [], isLoading: appsLoading } = useActionQuery("list-workspace-apps", { includeAgentCards: false, includeArchived: true });
35
33
  const { data: workspace } = useActionQuery("get-workspace-info", {}, { staleTime: 60_000 });
36
34
  const { data: templates = [], isLoading: templatesLoading } = useActionQuery("list-available-workspace-templates", {}, { refetchInterval: 5_000 });
37
35
  const ws = workspace;
@@ -1 +1 @@
1
- {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useActionMutation,\n useActionQuery,\n useT,\n} from \"@agent-native/core/client\";\nimport {\n IconApps,\n IconBrain,\n IconBrush,\n IconCalendarMonth,\n IconChartBar,\n IconChevronDown,\n IconClipboardList,\n IconEyeOff,\n IconFileText,\n IconLoader2,\n IconMail,\n IconPhoto,\n IconPlus,\n IconPresentation,\n IconScreenShare,\n IconSparkles,\n IconStack3,\n} from \"@tabler/icons-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { CreateAppPopover } from \"../../components/create-app-popover\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../../components/ui/collapsible\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { WorkspaceAppCard } from \"../../components/workspace-app-card\";\nimport { cn } from \"../../lib/utils\";\nimport type { WorkspaceAppSummary } from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Apps — Dispatch\" }];\n}\n\ninterface WorkspaceInfo {\n name: string | null;\n displayName: string | null;\n appCount: number;\n}\n\ninterface AvailableTemplate {\n name: string;\n label: string;\n hint: string;\n icon: string;\n color: string;\n colorRgb: string;\n core: boolean;\n}\n\nconst TEMPLATE_ICONS: Record<string, typeof IconMail> = {\n Mail: IconMail,\n CalendarMonth: IconCalendarMonth,\n FileText: IconFileText,\n Presentation: IconPresentation,\n ScreenShare: IconScreenShare,\n Brain: IconBrain,\n Photo: IconPhoto,\n ChartBar: IconChartBar,\n ClipboardList: IconClipboardList,\n Brush: IconBrush,\n};\n\nexport default function AppsRoute() {\n const t = useT();\n const [showHidden, setShowHidden] = useState(false);\n const [templatesOpen, setTemplatesOpen] = useState(false);\n const { data: apps = [], isLoading: appsLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false, includeArchived: true },\n {\n refetchInterval: 2_000,\n },\n );\n const { data: workspace } = useActionQuery(\n \"get-workspace-info\",\n {},\n { staleTime: 60_000 },\n );\n const { data: templates = [], isLoading: templatesLoading } = useActionQuery(\n \"list-available-workspace-templates\",\n {},\n { refetchInterval: 5_000 },\n );\n\n const ws = workspace as WorkspaceInfo | undefined;\n const workspaceLabel = ws?.displayName ?? ws?.name ?? null;\n const allApps = (apps as WorkspaceAppSummary[]).filter(\n (app) => !app.isDispatch,\n );\n const visibleApps = allApps.filter((app) => !app.archived);\n const archivedApps = allApps.filter((app) => app.archived);\n const typedTemplates = templates as AvailableTemplate[];\n const showAppSkeletons = appsLoading && allApps.length === 0;\n\n return (\n <DispatchShell\n title={t(\"dispatch.nav.apps\")}\n description={\n workspaceLabel\n ? t(\"dispatch.pages.appsDescriptionWithWorkspace\", {\n workspace: workspaceLabel,\n })\n : t(\"dispatch.pages.appsDescription\")\n }\n >\n <div className=\"space-y-8\">\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2\">\n <IconApps\n size={16}\n className=\"mt-0.5 shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {workspaceLabel\n ? t(\"dispatch.pages.appsInWorkspace\", {\n workspace: workspaceLabel,\n })\n : t(\"dispatch.pages.workspaceApps\")}\n </h2>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n {t(\"dispatch.pages.activeCount\", {\n count: visibleApps.length,\n })}\n {archivedApps.length > 0\n ? ` · ${t(\"dispatch.pages.hiddenCount\", {\n count: archivedApps.length,\n })}`\n : \"\"}\n </p>\n </div>\n </div>\n {visibleApps.length > 0 ? (\n <CreateAppPopover\n align=\"end\"\n trigger={\n <Button size=\"sm\">\n <IconPlus size={15} className=\"mr-1.5\" />\n {t(\"dispatch.pages.createApp\")}\n </Button>\n }\n />\n ) : null}\n </div>\n\n {showAppSkeletons ? (\n <AppsSkeletonGrid />\n ) : visibleApps.length > 0 ? (\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {visibleApps.map((app) => (\n <WorkspaceAppCard key={app.id} app={app} className=\"h-full\" />\n ))}\n </div>\n ) : (\n <EmptyAppsState />\n )}\n </section>\n\n {typedTemplates.length > 0 || templatesLoading ? (\n <Collapsible open={templatesOpen} onOpenChange={setTemplatesOpen}>\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-t pt-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconStack3\n size={16}\n className=\"shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.templates\")}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {templatesLoading\n ? t(\"dispatch.pages.checkingTemplates\")\n : t(\"dispatch.pages.templatesAvailable\", {\n count: typedTemplates.length,\n })}\n </p>\n </div>\n </div>\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-1.5\"\n >\n {templatesOpen\n ? t(\"dispatch.pages.hide\")\n : t(\"dispatch.pages.show\")}\n <IconChevronDown\n size={14}\n className={cn(\n \"transition-transform\",\n templatesOpen && \"rotate-180\",\n )}\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n {templatesLoading && typedTemplates.length === 0 ? (\n <AppsSkeletonGrid />\n ) : (\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {typedTemplates.map((template) => (\n <AddTemplateCard\n key={template.name}\n template={template}\n />\n ))}\n </div>\n )}\n </CollapsibleContent>\n </section>\n </Collapsible>\n ) : null}\n\n {archivedApps.length > 0 ? (\n <Collapsible open={showHidden} onOpenChange={setShowHidden}>\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-t pt-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconEyeOff\n size={16}\n className=\"shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.hiddenApps\")}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.hiddenAppCount\", {\n count: archivedApps.length,\n })}\n </p>\n </div>\n </div>\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-1.5\"\n >\n {showHidden\n ? t(\"dispatch.pages.hide\")\n : t(\"dispatch.pages.show\")}\n <IconChevronDown\n size={14}\n className={cn(\n \"transition-transform\",\n showHidden && \"rotate-180\",\n )}\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {archivedApps.map((app) => (\n <WorkspaceAppCard\n key={app.id}\n app={app}\n className=\"h-full\"\n />\n ))}\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n ) : null}\n </div>\n </DispatchShell>\n );\n}\n\nfunction AppsSkeletonGrid() {\n return (\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1 space-y-3\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n <div className=\"space-y-2 pt-1\">\n <Skeleton className=\"h-3 w-full\" />\n <Skeleton className=\"h-3 w-2/3\" />\n </div>\n </div>\n <Skeleton className=\"h-7 w-7 rounded-md\" />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction EmptyAppsState() {\n const t = useT();\n return (\n <div className=\"rounded-lg border border-dashed bg-card px-4 py-10 text-center\">\n <div className=\"mx-auto flex size-10 items-center justify-center rounded-lg bg-muted text-muted-foreground\">\n <IconApps size={18} />\n </div>\n <h3 className=\"mt-3 text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.noWorkspaceApps\")}\n </h3>\n <p className=\"mx-auto mt-1 max-w-sm text-sm text-muted-foreground\">\n {t(\"dispatch.pages.noWorkspaceAppsDescription\")}\n </p>\n <div className=\"mt-4\">\n <CreateAppPopover\n trigger={\n <Button size=\"sm\">\n <IconPlus size={15} className=\"mr-1.5\" />\n {t(\"dispatch.pages.createApp\")}\n </Button>\n }\n />\n </div>\n </div>\n );\n}\n\nfunction AddTemplateCard({ template }: { template: AvailableTemplate }) {\n const Icon = TEMPLATE_ICONS[template.icon] ?? IconSparkles;\n const scaffold = useActionMutation(\"scaffold-workspace-app\", {\n onSuccess: (result: any) => {\n toast.success(\n `Scaffolded apps/${result?.appId || template.name}. The gateway will pick it up shortly.`,\n );\n },\n onError: (err) => {\n toast.error(\n `Could not scaffold ${template.label}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n });\n\n return (\n <div className=\"group relative flex h-full min-h-36 items-stretch gap-3 rounded-lg border bg-card p-4 transition hover:border-foreground/30\">\n <div\n className=\"flex h-9 w-9 shrink-0 items-center justify-center self-start rounded-md\"\n style={{\n backgroundColor: `rgb(${template.colorRgb} / 0.12)`,\n color: template.color,\n }}\n >\n <Icon size={18} />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-foreground\">\n {template.label}\n </h3>\n </div>\n <p className=\"mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {template.hint}\n </p>\n <div className=\"mt-auto pt-3\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={scaffold.isPending}\n onClick={() => scaffold.mutate({ template: template.name })}\n >\n {scaffold.isPending ? (\n <>\n <IconLoader2 size={14} className=\"mr-1.5 animate-spin\" />\n Adding...\n </>\n ) : (\n <>\n <IconPlus size={14} className=\"mr-1.5\" />\n Add to workspace\n </>\n )}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useActionMutation,\n useActionQuery,\n useT,\n} from \"@agent-native/core/client\";\nimport {\n IconApps,\n IconBrain,\n IconBrush,\n IconCalendarMonth,\n IconChartBar,\n IconChevronDown,\n IconClipboardList,\n IconEyeOff,\n IconFileText,\n IconLoader2,\n IconMail,\n IconPhoto,\n IconPlus,\n IconPresentation,\n IconScreenShare,\n IconSparkles,\n IconStack3,\n} from \"@tabler/icons-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { CreateAppPopover } from \"../../components/create-app-popover\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../../components/ui/collapsible\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { WorkspaceAppCard } from \"../../components/workspace-app-card\";\nimport { cn } from \"../../lib/utils\";\nimport type { WorkspaceAppSummary } from \"../../lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Apps — Dispatch\" }];\n}\n\ninterface WorkspaceInfo {\n name: string | null;\n displayName: string | null;\n appCount: number;\n}\n\ninterface AvailableTemplate {\n name: string;\n label: string;\n hint: string;\n icon: string;\n color: string;\n colorRgb: string;\n core: boolean;\n}\n\nconst TEMPLATE_ICONS: Record<string, typeof IconMail> = {\n Mail: IconMail,\n CalendarMonth: IconCalendarMonth,\n FileText: IconFileText,\n Presentation: IconPresentation,\n ScreenShare: IconScreenShare,\n Brain: IconBrain,\n Photo: IconPhoto,\n ChartBar: IconChartBar,\n ClipboardList: IconClipboardList,\n Brush: IconBrush,\n};\n\nexport default function AppsRoute() {\n const t = useT();\n const [showHidden, setShowHidden] = useState(false);\n const [templatesOpen, setTemplatesOpen] = useState(false);\n const { data: apps = [], isLoading: appsLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false, includeArchived: true },\n );\n const { data: workspace } = useActionQuery(\n \"get-workspace-info\",\n {},\n { staleTime: 60_000 },\n );\n const { data: templates = [], isLoading: templatesLoading } = useActionQuery(\n \"list-available-workspace-templates\",\n {},\n { refetchInterval: 5_000 },\n );\n\n const ws = workspace as WorkspaceInfo | undefined;\n const workspaceLabel = ws?.displayName ?? ws?.name ?? null;\n const allApps = (apps as WorkspaceAppSummary[]).filter(\n (app) => !app.isDispatch,\n );\n const visibleApps = allApps.filter((app) => !app.archived);\n const archivedApps = allApps.filter((app) => app.archived);\n const typedTemplates = templates as AvailableTemplate[];\n const showAppSkeletons = appsLoading && allApps.length === 0;\n\n return (\n <DispatchShell\n title={t(\"dispatch.nav.apps\")}\n description={\n workspaceLabel\n ? t(\"dispatch.pages.appsDescriptionWithWorkspace\", {\n workspace: workspaceLabel,\n })\n : t(\"dispatch.pages.appsDescription\")\n }\n >\n <div className=\"space-y-8\">\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2\">\n <IconApps\n size={16}\n className=\"mt-0.5 shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {workspaceLabel\n ? t(\"dispatch.pages.appsInWorkspace\", {\n workspace: workspaceLabel,\n })\n : t(\"dispatch.pages.workspaceApps\")}\n </h2>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n {t(\"dispatch.pages.activeCount\", {\n count: visibleApps.length,\n })}\n {archivedApps.length > 0\n ? ` · ${t(\"dispatch.pages.hiddenCount\", {\n count: archivedApps.length,\n })}`\n : \"\"}\n </p>\n </div>\n </div>\n {visibleApps.length > 0 ? (\n <CreateAppPopover\n align=\"end\"\n trigger={\n <Button size=\"sm\">\n <IconPlus size={15} className=\"mr-1.5\" />\n {t(\"dispatch.pages.createApp\")}\n </Button>\n }\n />\n ) : null}\n </div>\n\n {showAppSkeletons ? (\n <AppsSkeletonGrid />\n ) : visibleApps.length > 0 ? (\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {visibleApps.map((app) => (\n <WorkspaceAppCard key={app.id} app={app} className=\"h-full\" />\n ))}\n </div>\n ) : (\n <EmptyAppsState />\n )}\n </section>\n\n {typedTemplates.length > 0 || templatesLoading ? (\n <Collapsible open={templatesOpen} onOpenChange={setTemplatesOpen}>\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-t pt-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconStack3\n size={16}\n className=\"shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.templates\")}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {templatesLoading\n ? t(\"dispatch.pages.checkingTemplates\")\n : t(\"dispatch.pages.templatesAvailable\", {\n count: typedTemplates.length,\n })}\n </p>\n </div>\n </div>\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-1.5\"\n >\n {templatesOpen\n ? t(\"dispatch.pages.hide\")\n : t(\"dispatch.pages.show\")}\n <IconChevronDown\n size={14}\n className={cn(\n \"transition-transform\",\n templatesOpen && \"rotate-180\",\n )}\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n {templatesLoading && typedTemplates.length === 0 ? (\n <AppsSkeletonGrid />\n ) : (\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {typedTemplates.map((template) => (\n <AddTemplateCard\n key={template.name}\n template={template}\n />\n ))}\n </div>\n )}\n </CollapsibleContent>\n </section>\n </Collapsible>\n ) : null}\n\n {archivedApps.length > 0 ? (\n <Collapsible open={showHidden} onOpenChange={setShowHidden}>\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-t pt-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconEyeOff\n size={16}\n className=\"shrink-0 text-muted-foreground\"\n />\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.hiddenApps\")}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.hiddenAppCount\", {\n count: archivedApps.length,\n })}\n </p>\n </div>\n </div>\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-1.5\"\n >\n {showHidden\n ? t(\"dispatch.pages.hide\")\n : t(\"dispatch.pages.show\")}\n <IconChevronDown\n size={14}\n className={cn(\n \"transition-transform\",\n showHidden && \"rotate-180\",\n )}\n />\n </Button>\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <div className=\"grid auto-rows-fr gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {archivedApps.map((app) => (\n <WorkspaceAppCard\n key={app.id}\n app={app}\n className=\"h-full\"\n />\n ))}\n </div>\n </CollapsibleContent>\n </section>\n </Collapsible>\n ) : null}\n </div>\n </DispatchShell>\n );\n}\n\nfunction AppsSkeletonGrid() {\n return (\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1 space-y-3\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n <div className=\"space-y-2 pt-1\">\n <Skeleton className=\"h-3 w-full\" />\n <Skeleton className=\"h-3 w-2/3\" />\n </div>\n </div>\n <Skeleton className=\"h-7 w-7 rounded-md\" />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction EmptyAppsState() {\n const t = useT();\n return (\n <div className=\"rounded-lg border border-dashed bg-card px-4 py-10 text-center\">\n <div className=\"mx-auto flex size-10 items-center justify-center rounded-lg bg-muted text-muted-foreground\">\n <IconApps size={18} />\n </div>\n <h3 className=\"mt-3 text-sm font-semibold text-foreground\">\n {t(\"dispatch.pages.noWorkspaceApps\")}\n </h3>\n <p className=\"mx-auto mt-1 max-w-sm text-sm text-muted-foreground\">\n {t(\"dispatch.pages.noWorkspaceAppsDescription\")}\n </p>\n <div className=\"mt-4\">\n <CreateAppPopover\n trigger={\n <Button size=\"sm\">\n <IconPlus size={15} className=\"mr-1.5\" />\n {t(\"dispatch.pages.createApp\")}\n </Button>\n }\n />\n </div>\n </div>\n );\n}\n\nfunction AddTemplateCard({ template }: { template: AvailableTemplate }) {\n const Icon = TEMPLATE_ICONS[template.icon] ?? IconSparkles;\n const scaffold = useActionMutation(\"scaffold-workspace-app\", {\n onSuccess: (result: any) => {\n toast.success(\n `Scaffolded apps/${result?.appId || template.name}. The gateway will pick it up shortly.`,\n );\n },\n onError: (err) => {\n toast.error(\n `Could not scaffold ${template.label}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n });\n\n return (\n <div className=\"group relative flex h-full min-h-36 items-stretch gap-3 rounded-lg border bg-card p-4 transition hover:border-foreground/30\">\n <div\n className=\"flex h-9 w-9 shrink-0 items-center justify-center self-start rounded-md\"\n style={{\n backgroundColor: `rgb(${template.colorRgb} / 0.12)`,\n color: template.color,\n }}\n >\n <Icon size={18} />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-foreground\">\n {template.label}\n </h3>\n </div>\n <p className=\"mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {template.hint}\n </p>\n <div className=\"mt-auto pt-3\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={scaffold.isPending}\n onClick={() => scaffold.mutate({ template: template.name })}\n >\n {scaffold.isPending ? (\n <>\n <IconLoader2 size={14} className=\"mr-1.5 animate-spin\" />\n Adding...\n </>\n ) : (\n <>\n <IconPlus size={14} className=\"mr-1.5\" />\n Add to workspace\n </>\n )}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -4,6 +4,6 @@ export function meta() {
4
4
  return [{ title: "Team — Dispatch" }];
5
5
  }
6
6
  export default function TeamRoute() {
7
- return _jsx(Navigate, { to: "/settings#team", replace: true });
7
+ return _jsx(Navigate, { to: "/settings#organization", replace: true });
8
8
  }
9
9
  //# sourceMappingURL=team.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"team.js","sourceRoot":"","sources":["../../../src/routes/pages/team.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,OAAO,KAAC,QAAQ,IAAC,EAAE,EAAC,gBAAgB,EAAC,OAAO,SAAG,CAAC;AAClD,CAAC","sourcesContent":["import { Navigate } from \"react-router\";\n\nexport function meta() {\n return [{ title: \"Team — Dispatch\" }];\n}\n\nexport default function TeamRoute() {\n return <Navigate to=\"/settings#team\" replace />;\n}\n"]}
1
+ {"version":3,"file":"team.js","sourceRoot":"","sources":["../../../src/routes/pages/team.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,OAAO,KAAC,QAAQ,IAAC,EAAE,EAAC,wBAAwB,EAAC,OAAO,SAAG,CAAC;AAC1D,CAAC","sourcesContent":["import { Navigate } from \"react-router\";\n\nexport function meta() {\n return [{ title: \"Team — Dispatch\" }];\n}\n\nexport default function TeamRoute() {\n return <Navigate to=\"/settings#organization\" replace />;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"usage-metrics-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/usage-metrics-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage-metrics-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/usage-metrics-store.ts"],"names":[],"mappings":"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"}
@@ -73,6 +73,17 @@ async function queryRows(sql, args = []) {
73
73
  return [];
74
74
  }
75
75
  }
76
+ async function initializeUsageMetricsTable(sinceMs) {
77
+ try {
78
+ // Initializes token_usage on fresh deployments before the read-only
79
+ // aggregate queries below. The fake owner avoids changing visible data.
80
+ await getUsageSummary({ ownerEmail: "__dispatch_metrics_init__", sinceMs });
81
+ }
82
+ catch {
83
+ // Metrics should still render an empty state if usage storage is locked,
84
+ // stale, or unavailable; each aggregate read below is already best-effort.
85
+ }
86
+ }
76
87
  async function getViewerOrgRole(orgId, email) {
77
88
  if (!orgId)
78
89
  return null;
@@ -204,9 +215,7 @@ export async function listDispatchUsageMetrics(input) {
204
215
  const sinceDays = Math.max(1, Math.min(365, input.sinceDays ?? 30));
205
216
  const sinceMs = Date.now() - sinceDays * DAY_MS;
206
217
  const billing = usageBillingForEngine(await detectUsageEngineName());
207
- // Initializes token_usage on fresh deployments before the read-only
208
- // aggregate queries below. The fake owner avoids changing visible data.
209
- await getUsageSummary({ ownerEmail: "__dispatch_metrics_init__", sinceMs });
218
+ await initializeUsageMetricsTable(sinceMs);
210
219
  const rawMembers = orgId
211
220
  ? await listOrgMembers(orgId)
212
221
  : await listSignedInUsers();
@@ -1 +1 @@
1
- {"version":3,"file":"usage-metrics-store.js","sourceRoot":"","sources":["../../../src/server/lib/usage-metrics-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,EACnB,8BAA8B,EAC9B,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EACL,eAAe,EACf,qBAAqB,GAEtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B,sBAAsB,EAAE,CAAC;AA8GzB,SAAS,WAAW,CAAC,GAA4B,EAAE,GAAW;IAC5D,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,WAAW,CAAC,GAA4B,EAAE,GAAW;IAC5D,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAA4B,EAC5B,GAAW;IAEX,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3D,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,IAAI,cAAc,CAAC;AACnC,CAAC;AAED,SAAS,eAAe,CAAC,KAAgC;IACvD,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO,cAAc,CAAC;IAChC,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC7B,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAC1C,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,OAAO;QACL,GAAG,SAAS,CAAC,uBAAuB,CAAC;QACrC,GAAG,SAAS,CAAC,uBAAuB,CAAC;QACrC,GAAG,SAAS,CAAC,8BAA8B,CAAC;KAC7C,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACzB,CAAC;AAED,KAAK,UAAU,qBAAqB;IAClC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,MAAM,UAAU,CAAC,cAAc,CAAC,CAExC,CAAC;QACT,IAAI,8BAA8B,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,OAAQ,MAA6B,CAAC,MAAM,CAAC;QAC/C,CAAC;QACD,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACjD,IAAI,KAAK,IAAI,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;gBACjD,OAAO,MAAM,CAAC,MAAM,CAAC;YACvB,CAAC;QACH,CAAC;QAED,MAAM,gBAAgB,GAAG,MAAM,2BAA2B,EAAE,CAAC;QAC7D,IAAI,gBAAgB;YAAE,OAAO,gBAAgB,CAAC,IAAI,CAAC;QAEnD,OAAO,mBAAmB,EAAE,EAAE,IAAI,IAAI,IAAI,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,GAAW,EACX,IAAI,GAAc,EAAE;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC,IAAW,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,KAAoB,EACpB,KAAa;IAEb,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,MAAM,SAAS,CAC1B,4EAA4E,EAC5E,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAC7B,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC3B,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,KAAoB;IAChD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG,MAAM,SAAS,CAC1B,qGAAqG,EACrG,CAAC,KAAK,CAAC,CACR,CAAC;IACF,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACb,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE;QACvC,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI;QACtC,QAAQ,EAAE,mBAAmB,CAAC,GAAG,EAAE,WAAW,CAAC;KAChD,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,MAAM,QAAQ,GAAG,MAAM,SAAS,CAC9B,2EAA2E,CAC5E,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,QAAQ;aACZ,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACb,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE;YACvC,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,mBAAmB,CAAC,GAAG,EAAE,WAAW,CAAC;SAChD,CAAC,CAAC;aACF,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,SAAS,CAC/B,uDAAuD,CACxD,CAAC;IACF,MAAM,UAAU,GAAG,MAAM,SAAS,CAChC,wDAAwD,CACzD,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC;QAChD,IAAI,GAAG,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,KAAK;QACL,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,UAAU,CACjB,OAAe,EACf,YAAsB;IAEtB,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACvD,CAAC;IACD,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO;QACL,KAAK,EAAE,uCAAuC,YAAY,GAAG;QAC7D,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,OAAe,EACf,YAAsB;IAEtB,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACvD,CAAC;IACD,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO;QACL,KAAK,EAAE,uCAAuC,YAAY,GAAG;QAC7D,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAA4B;IACjD,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,OAAO;QACL,GAAG;QACH,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;QACvB,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,GAAG;QAC9C,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC;QAChC,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC;QACzC,WAAW,EAAE,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7C,YAAY,EAAE,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC;QAC/C,eAAe,EAAE,WAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC;QACtD,gBAAgB,EAAE,WAAW,CAAC,GAAG,EAAE,oBAAoB,CAAC;QACxD,WAAW,EAAE,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7C,YAAY,EAAE,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,CAAC;KACzD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,gBAAwB,EACxB,KAAa,EACb,IAAe,EACf,KAAa;IAEb,MAAM,IAAI,GAAG,MAAM,SAAS,CAC1B,UAAU,gBAAgB;;;;;;;;;;;cAWhB,KAAK;iBACF,gBAAgB;;cAEnB,EACV,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CACjB,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AACjC,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,KAAa,EACb,IAAe;IAEf,MAAM,IAAI,GAAG,MAAM,SAAS,CAC1B;;;;;cAKU,KAAK;2BACQ,EACvB,IAAI,CACL,CAAC;IACF,OAAO,IAAI,GAAG,CACZ,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;QAChB,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC;QAC/B;YACE,OAAO,EAAE,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC;YACpC,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC;YACtC,UAAU,EAAE,mBAAmB,CAAC,GAAG,EAAE,cAAc,CAAC;SACrD;KACF,CAAC,CACH,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,oBAAoB;IAKjC,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,WAAW,CAAC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACpE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,KAE9C;IACC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,MAAM,CAAC;IAChD,MAAM,OAAO,GAAG,qBAAqB,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC;IAErE,oEAAoE;IACpE,wEAAwE;IACxE,MAAM,eAAe,CAAC,EAAE,UAAU,EAAE,2BAA2B,EAAE,OAAO,EAAE,CAAC,CAAC;IAE5E,MAAM,UAAU,GAAG,KAAK;QACtB,CAAC,CAAC,MAAM,cAAc,CAAC,KAAK,CAAC;QAC7B,CAAC,CAAC,MAAM,iBAAiB,EAAE,CAAC;IAC9B,MAAM,OAAO,GACX,KAAK,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAC9B,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAChD,CAAC,CAAC,UAAU,CAAC;IACjB,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,CAC9D,CAAC;IACF,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAEnD,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,GACtE,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;QAC/C,SAAS,CACP;;;;;;;;;;kBAUU,KAAK,CAAC,KAAK,EAAE,EACvB,KAAK,CAAC,IAAI,CACX;QACD,YAAY,CACV,2CAA2C,EAC3C,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,IAAI,EACV,EAAE,CACH;QACD,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACxD,YAAY,CACV,qCAAqC,EACrC,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,IAAI,EACV,EAAE,CACH;QACD,YAAY,CACV,wCAAwC,EACxC,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,IAAI,EACV,EAAE,CACH;QACD,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC;KAC3C,CAAC,CAAC;IAEL,MAAM,UAAU,GAAG,MAAM,SAAS,CAChC;;;;cAIU,KAAK,CAAC,KAAK;;+CAEsB,EAC3C,KAAK,CAAC,IAAI,CACX,CAAC;IACF,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC9C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IACrD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC;QAC9B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI;YACzC,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,IAAI;SACjB,CAAC;QACF,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;YACxB,GAAG,MAAM;YACT,UAAU;YACV,WAAW,EAAE,KAAK,CAAC,OAAO;YAC1B,YAAY,EAAE,KAAK,CAAC,QAAQ;YAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI;YAC5C,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;QAC5C,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QACxC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;YACxB,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,UAAU;YACjB,UAAU;YACV,SAAS,EAAE,CAAC;YACZ,KAAK,EAAE,CAAC;YACR,SAAS,EAAE,CAAC;YACZ,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;YACf,eAAe,EAAE,CAAC;YAClB,gBAAgB,EAAE,CAAC;YACnB,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,KAAK,CAAC,UAAU;YAC9B,WAAW,EAAE,KAAK,CAAC,OAAO;YAC1B,YAAY,EAAE,KAAK,CAAC,QAAQ;YAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAC7B;;cAEU,KAAK,CAAC,KAAK;8BACK,EAC1B,KAAK,CAAC,IAAI,CACX,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAGrB,CAAC;IACJ,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;aAClD,WAAW,EAAE;aACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI;YACpC,QAAQ,EAAE,CAAC;YACX,KAAK,EAAE,CAAC;YACR,SAAS,EAAE,CAAC;YACZ,KAAK,EAAE,IAAI,GAAG,EAAU;SACzB,CAAC;QACF,OAAO,CAAC,QAAQ,IAAI,WAAW,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QACxD,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QACnB,IAAI,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,MAAM;YAAE,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;QACnD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvB,IAAI;QACJ,SAAS,EAAE,KAAK,CAAC,QAAQ,GAAG,GAAG;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;KAC9B,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhD,MAAM,UAAU,GAAG,MAAM,SAAS,CAChC;;;;cAIU,KAAK,CAAC,KAAK;;eAEV,EACX,KAAK,CAAC,IAAI,CACX,CAAC;IACF,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtC,EAAE,EAAE,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAC1B,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC;QACzC,UAAU,EAAE,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC;QAC3C,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,cAAc;QAC9C,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM;QAC1C,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,SAAS;QAC7C,WAAW,EAAE,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC;QAC7C,YAAY,EAAE,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC;QAC/C,eAAe,EAAE,WAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC;QACtD,gBAAgB,EAAE,WAAW,CAAC,GAAG,EAAE,oBAAoB,CAAC;QACxD,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,iBAAiB,CAAC,GAAG,GAAG;KACrD,CAAC,CAAC,CAAC;IAEJ,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAC7D,CAAC;IACF,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC;IACrD,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACjC,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/D,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,WAAW;YACX,WAAW;YACX,WAAW;YACX,cAAc,EAAE,WAAW,EAAE,WAAW,IAAI,CAAC;YAC7C,UAAU,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;YACnC,SAAS,EAAE,WAAW,EAAE,SAAS,IAAI,CAAC;YACtC,SAAS,EAAE,WAAW,EAAE,SAAS,IAAI,CAAC;YACtC,YAAY,EAAE,WAAW,EAAE,YAAY,IAAI,IAAI;SACtB,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,gBAAgB,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACrD,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO;QACpC,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ;KACxC,CAAC,EACF,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAC5B,CAAC;IAEF,OAAO;QACL,OAAO;QACP,OAAO;QACP,SAAS;QACT,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;QACvB,MAAM,EAAE;YACN,WAAW;YACX,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM;YACtC,UAAU,EAAE,WAAW;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,GAAG;YACjD,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;YACnC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC;YAC5C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC;YAChD,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC;YAClD,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC;YACzD,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC;YAC3D,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC;YAChD,WAAW,EAAE,gBAAgB,CAAC,OAAO;YACrC,YAAY,EAAE,gBAAgB,CAAC,QAAQ;YACvC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM;SAC5D;QACD,KAAK;QACL,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC5C,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;gBAAE,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;YAClE,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC;QACF,OAAO;QACP,OAAO;QACP,KAAK;QACL,SAAS;QACT,MAAM;KACP,CAAC;AACJ,CAAC","sourcesContent":["import {\n detectEngineFromEnv,\n detectEngineFromUserSecrets,\n getAgentEngineEntry,\n isAgentEngineSettingConfigured,\n isStoredEngineUsable,\n registerBuiltinEngines,\n} from \"@agent-native/core/agent/engine\";\nimport { getDbExec } from \"@agent-native/core/db\";\nimport { getSetting } from \"@agent-native/core/settings\";\nimport {\n getUsageSummary,\n usageBillingForEngine,\n type UsageBillingMode,\n} from \"@agent-native/core/usage\";\n\nimport {\n listWorkspaceApps,\n type WorkspaceAppSummary,\n} from \"./app-creation-store.js\";\nimport { currentOrgId, currentOwnerEmail } from \"./dispatch-store.js\";\n\nconst DAY_MS = 86_400_000;\n\nregisterBuiltinEngines();\n\nexport interface UsageMetricBucket {\n key: string;\n label: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n activeUsers: number;\n lastActiveAt: number | null;\n}\n\nexport interface UserUsageMetric extends UsageMetricBucket {\n ownerEmail: string;\n chatThreads: number;\n chatMessages: number;\n lastChatAt: number | null;\n topApp: string | null;\n role: string | null;\n}\n\nexport interface AppAccessMetric {\n id: string;\n name: string;\n path: string;\n status: WorkspaceAppSummary[\"status\"];\n statusLabel?: string;\n isDispatch: boolean;\n accessModel: \"workspace\" | \"solo\";\n accessLabel: string;\n accessUsers: number;\n usersWithUsage: number;\n usageCalls: number;\n chatCalls: number;\n costCents: number;\n lastActiveAt: number | null;\n}\n\nexport interface DailyUsageMetric {\n date: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n activeUsers: number;\n}\n\nexport interface RecentUsageMetric {\n id: number;\n createdAt: number;\n ownerEmail: string;\n app: string;\n label: string;\n model: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n costCents: number;\n}\n\nexport interface DispatchUsageMetrics {\n billing: UsageBillingMode;\n sinceMs: number;\n sinceDays: number;\n generatedAt: number;\n access: {\n viewerEmail: string;\n orgId: string | null;\n role: string | null;\n scope: \"organization\" | \"solo\";\n totalUsers: number;\n };\n totals: {\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n activeUsers: number;\n chatThreads: number;\n chatMessages: number;\n workspaceApps: number;\n };\n byApp: UsageMetricBucket[];\n byUser: UserUsageMetric[];\n byLabel: UsageMetricBucket[];\n byModel: UsageMetricBucket[];\n daily: DailyUsageMetric[];\n appAccess: AppAccessMetric[];\n recent: RecentUsageMetric[];\n}\n\ninterface MemberRecord {\n email: string;\n role: string | null;\n joinedAt: number | null;\n}\n\ninterface ChatStats {\n threads: number;\n messages: number;\n lastChatAt: number | null;\n}\n\nfunction numberField(row: Record<string, unknown>, key: string): number {\n return Number(row[key] ?? 0) || 0;\n}\n\nfunction stringField(row: Record<string, unknown>, key: string): string {\n return String(row[key] ?? \"\");\n}\n\nfunction nullableNumberField(\n row: Record<string, unknown>,\n key: string,\n): number | null {\n const value = row[key];\n if (value == null) return null;\n const numberValue = Number(value);\n return Number.isFinite(numberValue) ? numberValue : null;\n}\n\nfunction labelForKey(value: string): string {\n const trimmed = value.trim();\n return trimmed || \"Unattributed\";\n}\n\nfunction normalizeAppKey(value: string | null | undefined): string {\n const raw = (value ?? \"\").trim().toLowerCase();\n if (!raw) return \"unattributed\";\n return raw.replace(/^agent-native-/, \"\");\n}\n\nfunction envEmails(name: string): string[] {\n return (process.env[name] ?? \"\")\n .split(\",\")\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction isEnvAdmin(email: string): boolean {\n const normalized = email.trim().toLowerCase();\n return [\n ...envEmails(\"DISPATCH_ADMIN_EMAILS\"),\n ...envEmails(\"WORKSPACE_OWNER_EMAIL\"),\n ...envEmails(\"DISPATCH_DEFAULT_OWNER_EMAIL\"),\n ].includes(normalized);\n}\n\nasync function detectUsageEngineName(): Promise<string | null> {\n try {\n const stored = (await getSetting(\"agent-engine\")) as {\n engine?: string;\n } | null;\n if (isAgentEngineSettingConfigured(stored)) {\n return (stored as { engine: string }).engine;\n }\n if (stored && typeof stored.engine === \"string\") {\n const entry = getAgentEngineEntry(stored.engine);\n if (entry && isStoredEngineUsable(stored, entry)) {\n return stored.engine;\n }\n }\n\n const detectedFromUser = await detectEngineFromUserSecrets();\n if (detectedFromUser) return detectedFromUser.name;\n\n return detectEngineFromEnv()?.name ?? null;\n } catch {\n return null;\n }\n}\n\nasync function queryRows<T extends Record<string, unknown>>(\n sql: string,\n args: unknown[] = [],\n): Promise<T[]> {\n try {\n const result = await getDbExec().execute({ sql, args });\n return result.rows as T[];\n } catch {\n return [];\n }\n}\n\nasync function getViewerOrgRole(\n orgId: string | null,\n email: string,\n): Promise<string | null> {\n if (!orgId) return null;\n const rows = await queryRows<{ role?: string }>(\n `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n [orgId, email.toLowerCase()],\n );\n const role = rows[0]?.role;\n return typeof role === \"string\" ? role : null;\n}\n\nasync function listOrgMembers(orgId: string | null): Promise<MemberRecord[]> {\n if (!orgId) return [];\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT email, role, joined_at AS joined_at FROM org_members WHERE org_id = ? ORDER BY joined_at ASC`,\n [orgId],\n );\n return rows\n .map((row) => ({\n email: stringField(row, \"email\").trim(),\n role: stringField(row, \"role\") || null,\n joinedAt: nullableNumberField(row, \"joined_at\"),\n }))\n .filter((member) => member.email);\n}\n\nasync function listSignedInUsers(): Promise<MemberRecord[]> {\n const authRows = await queryRows<Record<string, unknown>>(\n `SELECT email, created_at AS joined_at FROM \"user\" ORDER BY created_at ASC`,\n );\n if (authRows.length > 0) {\n return authRows\n .map((row) => ({\n email: stringField(row, \"email\").trim(),\n role: null,\n joinedAt: nullableNumberField(row, \"joined_at\"),\n }))\n .filter((member) => member.email);\n }\n\n const usageRows = await queryRows<{ email?: string }>(\n `SELECT DISTINCT owner_email AS email FROM token_usage`,\n );\n const threadRows = await queryRows<{ email?: string }>(\n `SELECT DISTINCT owner_email AS email FROM chat_threads`,\n );\n const emails = new Set<string>();\n for (const row of [...usageRows, ...threadRows]) {\n if (row.email) emails.add(row.email);\n }\n return [...emails].sort().map((email) => ({\n email,\n role: null,\n joinedAt: null,\n }));\n}\n\nfunction usageScope(\n sinceMs: number,\n memberEmails: string[],\n): { where: string; args: unknown[] } {\n if (memberEmails.length === 0) {\n return { where: \"created_at >= ?\", args: [sinceMs] };\n }\n const placeholders = memberEmails.map(() => \"?\").join(\", \");\n return {\n where: `created_at >= ? AND owner_email IN (${placeholders})`,\n args: [sinceMs, ...memberEmails],\n };\n}\n\nfunction threadScope(\n sinceMs: number,\n memberEmails: string[],\n): { where: string; args: unknown[] } {\n if (memberEmails.length === 0) {\n return { where: \"updated_at >= ?\", args: [sinceMs] };\n }\n const placeholders = memberEmails.map(() => \"?\").join(\", \");\n return {\n where: `updated_at >= ? AND owner_email IN (${placeholders})`,\n args: [sinceMs, ...memberEmails],\n };\n}\n\nfunction bucketFromRow(row: Record<string, unknown>): UsageMetricBucket {\n const key = stringField(row, \"k\");\n return {\n key,\n label: labelForKey(key),\n costCents: numberField(row, \"cost_x100\") / 100,\n calls: numberField(row, \"calls\"),\n chatCalls: numberField(row, \"chat_calls\"),\n inputTokens: numberField(row, \"input_tokens\"),\n outputTokens: numberField(row, \"output_tokens\"),\n cacheReadTokens: numberField(row, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(row, \"cache_write_tokens\"),\n activeUsers: numberField(row, \"active_users\"),\n lastActiveAt: nullableNumberField(row, \"last_active_at\"),\n };\n}\n\nasync function usageBuckets(\n columnExpression: string,\n where: string,\n args: unknown[],\n limit: number,\n): Promise<UsageMetricBucket[]> {\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT ${columnExpression} AS k,\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100,\n COUNT(*) AS calls,\n SUM(CASE WHEN label = 'chat' THEN 1 ELSE 0 END) AS chat_calls,\n COALESCE(SUM(input_tokens), 0) AS input_tokens,\n COALESCE(SUM(output_tokens), 0) AS output_tokens,\n COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,\n COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,\n COUNT(DISTINCT owner_email) AS active_users,\n MAX(created_at) AS last_active_at\n FROM token_usage\n WHERE ${where}\n GROUP BY ${columnExpression}\n ORDER BY cost_x100 DESC\n LIMIT ?`,\n [...args, limit],\n );\n return rows.map(bucketFromRow);\n}\n\nasync function loadChatStats(\n where: string,\n args: unknown[],\n): Promise<Map<string, ChatStats>> {\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT owner_email AS owner_email,\n COUNT(*) AS threads,\n COALESCE(SUM(message_count), 0) AS messages,\n MAX(updated_at) AS last_chat_at\n FROM chat_threads\n WHERE ${where}\n GROUP BY owner_email`,\n args,\n );\n return new Map(\n rows.map((row) => [\n stringField(row, \"owner_email\"),\n {\n threads: numberField(row, \"threads\"),\n messages: numberField(row, \"messages\"),\n lastChatAt: nullableNumberField(row, \"last_chat_at\"),\n },\n ]),\n );\n}\n\nasync function assertCanViewMetrics(): Promise<{\n viewerEmail: string;\n orgId: string | null;\n role: string | null;\n}> {\n const viewerEmail = currentOwnerEmail();\n const orgId = currentOrgId();\n const role = await getViewerOrgRole(orgId, viewerEmail);\n if (isEnvAdmin(viewerEmail) || role === \"owner\" || role === \"admin\") {\n return { viewerEmail, orgId, role };\n }\n if (!orgId) {\n return { viewerEmail, orgId, role };\n }\n throw new Error(\n \"Only organization owners and admins can view workspace usage metrics.\",\n );\n}\n\nexport async function listDispatchUsageMetrics(input: {\n sinceDays?: number;\n}): Promise<DispatchUsageMetrics> {\n const { viewerEmail, orgId, role } = await assertCanViewMetrics();\n const sinceDays = Math.max(1, Math.min(365, input.sinceDays ?? 30));\n const sinceMs = Date.now() - sinceDays * DAY_MS;\n const billing = usageBillingForEngine(await detectUsageEngineName());\n\n // Initializes token_usage on fresh deployments before the read-only\n // aggregate queries below. The fake owner avoids changing visible data.\n await getUsageSummary({ ownerEmail: \"__dispatch_metrics_init__\", sinceMs });\n\n const rawMembers = orgId\n ? await listOrgMembers(orgId)\n : await listSignedInUsers();\n const members =\n orgId && rawMembers.length === 0\n ? [{ email: viewerEmail, role, joinedAt: null }]\n : rawMembers;\n const memberEmails = orgId ? members.map((member) => member.email) : [];\n const memberByEmail = new Map(\n members.map((member) => [member.email.toLowerCase(), member]),\n );\n const usage = usageScope(sinceMs, memberEmails);\n const threads = threadScope(sinceMs, memberEmails);\n\n const [apps, totalsRows, byApp, byUserBase, byLabel, byModel, chatStats] =\n await Promise.all([\n listWorkspaceApps({ includeAgentCards: false }),\n queryRows<Record<string, unknown>>(\n `SELECT\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100,\n COUNT(*) AS calls,\n SUM(CASE WHEN label = 'chat' THEN 1 ELSE 0 END) AS chat_calls,\n COALESCE(SUM(input_tokens), 0) AS input_tokens,\n COALESCE(SUM(output_tokens), 0) AS output_tokens,\n COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,\n COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,\n COUNT(DISTINCT owner_email) AS active_users\n FROM token_usage\n WHERE ${usage.where}`,\n usage.args,\n ),\n usageBuckets(\n `COALESCE(NULLIF(app, ''), 'unattributed')`,\n usage.where,\n usage.args,\n 20,\n ),\n usageBuckets(\"owner_email\", usage.where, usage.args, 50),\n usageBuckets(\n `COALESCE(NULLIF(label, ''), 'chat')`,\n usage.where,\n usage.args,\n 20,\n ),\n usageBuckets(\n `COALESCE(NULLIF(model, ''), 'unknown')`,\n usage.where,\n usage.args,\n 20,\n ),\n loadChatStats(threads.where, threads.args),\n ]);\n\n const topAppRows = await queryRows<Record<string, unknown>>(\n `SELECT owner_email AS owner_email,\n COALESCE(NULLIF(app, ''), 'unattributed') AS app,\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100\n FROM token_usage\n WHERE ${usage.where}\n GROUP BY owner_email, COALESCE(NULLIF(app, ''), 'unattributed')\n ORDER BY owner_email ASC, cost_x100 DESC`,\n usage.args,\n );\n const topAppByUser = new Map<string, string>();\n for (const row of topAppRows) {\n const email = stringField(row, \"owner_email\");\n if (!topAppByUser.has(email)) {\n topAppByUser.set(email, stringField(row, \"app\"));\n }\n }\n\n const byUserMap = new Map<string, UserUsageMetric>();\n for (const bucket of byUserBase) {\n const ownerEmail = bucket.key;\n const stats = chatStats.get(ownerEmail) ?? {\n threads: 0,\n messages: 0,\n lastChatAt: null,\n };\n const member = memberByEmail.get(ownerEmail.toLowerCase());\n byUserMap.set(ownerEmail, {\n ...bucket,\n ownerEmail,\n chatThreads: stats.threads,\n chatMessages: stats.messages,\n lastChatAt: stats.lastChatAt,\n topApp: topAppByUser.get(ownerEmail) ?? null,\n role: member?.role ?? null,\n });\n }\n for (const [ownerEmail, stats] of chatStats) {\n if (byUserMap.has(ownerEmail)) continue;\n const member = memberByEmail.get(ownerEmail.toLowerCase());\n byUserMap.set(ownerEmail, {\n key: ownerEmail,\n label: ownerEmail,\n ownerEmail,\n costCents: 0,\n calls: 0,\n chatCalls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n activeUsers: 1,\n lastActiveAt: stats.lastChatAt,\n chatThreads: stats.threads,\n chatMessages: stats.messages,\n lastChatAt: stats.lastChatAt,\n topApp: null,\n role: member?.role ?? null,\n });\n }\n\n const dayRows = await queryRows<Record<string, unknown>>(\n `SELECT created_at, owner_email, label, cost_cents_x100\n FROM token_usage\n WHERE ${usage.where}\n ORDER BY created_at ASC`,\n usage.args,\n );\n const dailyMap = new Map<\n string,\n { costX100: number; calls: number; chatCalls: number; users: Set<string> }\n >();\n for (const row of dayRows) {\n const date = new Date(numberField(row, \"created_at\"))\n .toISOString()\n .slice(0, 10);\n const current = dailyMap.get(date) ?? {\n costX100: 0,\n calls: 0,\n chatCalls: 0,\n users: new Set<string>(),\n };\n current.costX100 += numberField(row, \"cost_cents_x100\");\n current.calls += 1;\n if (stringField(row, \"label\") === \"chat\") current.chatCalls += 1;\n current.users.add(stringField(row, \"owner_email\"));\n dailyMap.set(date, current);\n }\n const daily = [...dailyMap.entries()]\n .map(([date, value]) => ({\n date,\n costCents: value.costX100 / 100,\n calls: value.calls,\n chatCalls: value.chatCalls,\n activeUsers: value.users.size,\n }))\n .sort((a, b) => a.date.localeCompare(b.date));\n\n const recentRows = await queryRows<Record<string, unknown>>(\n `SELECT id, created_at, owner_email, app, label, model,\n input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,\n cost_cents_x100\n FROM token_usage\n WHERE ${usage.where}\n ORDER BY created_at DESC\n LIMIT 50`,\n usage.args,\n );\n const recent = recentRows.map((row) => ({\n id: numberField(row, \"id\"),\n createdAt: numberField(row, \"created_at\"),\n ownerEmail: stringField(row, \"owner_email\"),\n app: stringField(row, \"app\") || \"unattributed\",\n label: stringField(row, \"label\") || \"chat\",\n model: stringField(row, \"model\") || \"unknown\",\n inputTokens: numberField(row, \"input_tokens\"),\n outputTokens: numberField(row, \"output_tokens\"),\n cacheReadTokens: numberField(row, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(row, \"cache_write_tokens\"),\n costCents: numberField(row, \"cost_cents_x100\") / 100,\n }));\n\n const appUsageByKey = new Map(\n byApp.map((bucket) => [normalizeAppKey(bucket.key), bucket]),\n );\n const accessUsers = members.length || byUserMap.size;\n const accessModel = orgId ? \"workspace\" : \"solo\";\n const accessLabel = orgId ? \"Workspace members\" : \"Signed-in users\";\n const appAccess = apps.map((app) => {\n const usageBucket = appUsageByKey.get(normalizeAppKey(app.id));\n return {\n id: app.id,\n name: app.name,\n path: app.path,\n status: app.status,\n statusLabel: app.statusLabel,\n isDispatch: app.isDispatch,\n accessModel,\n accessLabel,\n accessUsers,\n usersWithUsage: usageBucket?.activeUsers ?? 0,\n usageCalls: usageBucket?.calls ?? 0,\n chatCalls: usageBucket?.chatCalls ?? 0,\n costCents: usageBucket?.costCents ?? 0,\n lastActiveAt: usageBucket?.lastActiveAt ?? null,\n } satisfies AppAccessMetric;\n });\n\n const totals = totalsRows[0] ?? {};\n const chatThreadTotals = [...chatStats.values()].reduce(\n (acc, value) => ({\n threads: acc.threads + value.threads,\n messages: acc.messages + value.messages,\n }),\n { threads: 0, messages: 0 },\n );\n\n return {\n billing,\n sinceMs,\n sinceDays,\n generatedAt: Date.now(),\n access: {\n viewerEmail,\n orgId,\n role,\n scope: orgId ? \"organization\" : \"solo\",\n totalUsers: accessUsers,\n },\n totals: {\n costCents: numberField(totals, \"cost_x100\") / 100,\n calls: numberField(totals, \"calls\"),\n chatCalls: numberField(totals, \"chat_calls\"),\n inputTokens: numberField(totals, \"input_tokens\"),\n outputTokens: numberField(totals, \"output_tokens\"),\n cacheReadTokens: numberField(totals, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(totals, \"cache_write_tokens\"),\n activeUsers: numberField(totals, \"active_users\"),\n chatThreads: chatThreadTotals.threads,\n chatMessages: chatThreadTotals.messages,\n workspaceApps: apps.filter((app) => !app.isDispatch).length,\n },\n byApp,\n byUser: [...byUserMap.values()].sort((a, b) => {\n if (b.costCents !== a.costCents) return b.costCents - a.costCents;\n return (b.lastActiveAt ?? 0) - (a.lastActiveAt ?? 0);\n }),\n byLabel,\n byModel,\n daily,\n appAccess,\n recent,\n };\n}\n"]}
1
+ {"version":3,"file":"usage-metrics-store.js","sourceRoot":"","sources":["../../../src/server/lib/usage-metrics-store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n detectEngineFromEnv,\n detectEngineFromUserSecrets,\n getAgentEngineEntry,\n isAgentEngineSettingConfigured,\n isStoredEngineUsable,\n registerBuiltinEngines,\n} from \"@agent-native/core/agent/engine\";\nimport { getDbExec } from \"@agent-native/core/db\";\nimport { getSetting } from \"@agent-native/core/settings\";\nimport {\n getUsageSummary,\n usageBillingForEngine,\n type UsageBillingMode,\n} from \"@agent-native/core/usage\";\n\nimport {\n listWorkspaceApps,\n type WorkspaceAppSummary,\n} from \"./app-creation-store.js\";\nimport { currentOrgId, currentOwnerEmail } from \"./dispatch-store.js\";\n\nconst DAY_MS = 86_400_000;\n\nregisterBuiltinEngines();\n\nexport interface UsageMetricBucket {\n key: string;\n label: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n activeUsers: number;\n lastActiveAt: number | null;\n}\n\nexport interface UserUsageMetric extends UsageMetricBucket {\n ownerEmail: string;\n chatThreads: number;\n chatMessages: number;\n lastChatAt: number | null;\n topApp: string | null;\n role: string | null;\n}\n\nexport interface AppAccessMetric {\n id: string;\n name: string;\n path: string;\n status: WorkspaceAppSummary[\"status\"];\n statusLabel?: string;\n isDispatch: boolean;\n accessModel: \"workspace\" | \"solo\";\n accessLabel: string;\n accessUsers: number;\n usersWithUsage: number;\n usageCalls: number;\n chatCalls: number;\n costCents: number;\n lastActiveAt: number | null;\n}\n\nexport interface DailyUsageMetric {\n date: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n activeUsers: number;\n}\n\nexport interface RecentUsageMetric {\n id: number;\n createdAt: number;\n ownerEmail: string;\n app: string;\n label: string;\n model: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n costCents: number;\n}\n\nexport interface DispatchUsageMetrics {\n billing: UsageBillingMode;\n sinceMs: number;\n sinceDays: number;\n generatedAt: number;\n access: {\n viewerEmail: string;\n orgId: string | null;\n role: string | null;\n scope: \"organization\" | \"solo\";\n totalUsers: number;\n };\n totals: {\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n activeUsers: number;\n chatThreads: number;\n chatMessages: number;\n workspaceApps: number;\n };\n byApp: UsageMetricBucket[];\n byUser: UserUsageMetric[];\n byLabel: UsageMetricBucket[];\n byModel: UsageMetricBucket[];\n daily: DailyUsageMetric[];\n appAccess: AppAccessMetric[];\n recent: RecentUsageMetric[];\n}\n\ninterface MemberRecord {\n email: string;\n role: string | null;\n joinedAt: number | null;\n}\n\ninterface ChatStats {\n threads: number;\n messages: number;\n lastChatAt: number | null;\n}\n\nfunction numberField(row: Record<string, unknown>, key: string): number {\n return Number(row[key] ?? 0) || 0;\n}\n\nfunction stringField(row: Record<string, unknown>, key: string): string {\n return String(row[key] ?? \"\");\n}\n\nfunction nullableNumberField(\n row: Record<string, unknown>,\n key: string,\n): number | null {\n const value = row[key];\n if (value == null) return null;\n const numberValue = Number(value);\n return Number.isFinite(numberValue) ? numberValue : null;\n}\n\nfunction labelForKey(value: string): string {\n const trimmed = value.trim();\n return trimmed || \"Unattributed\";\n}\n\nfunction normalizeAppKey(value: string | null | undefined): string {\n const raw = (value ?? \"\").trim().toLowerCase();\n if (!raw) return \"unattributed\";\n return raw.replace(/^agent-native-/, \"\");\n}\n\nfunction envEmails(name: string): string[] {\n return (process.env[name] ?? \"\")\n .split(\",\")\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction isEnvAdmin(email: string): boolean {\n const normalized = email.trim().toLowerCase();\n return [\n ...envEmails(\"DISPATCH_ADMIN_EMAILS\"),\n ...envEmails(\"WORKSPACE_OWNER_EMAIL\"),\n ...envEmails(\"DISPATCH_DEFAULT_OWNER_EMAIL\"),\n ].includes(normalized);\n}\n\nasync function detectUsageEngineName(): Promise<string | null> {\n try {\n const stored = (await getSetting(\"agent-engine\")) as {\n engine?: string;\n } | null;\n if (isAgentEngineSettingConfigured(stored)) {\n return (stored as { engine: string }).engine;\n }\n if (stored && typeof stored.engine === \"string\") {\n const entry = getAgentEngineEntry(stored.engine);\n if (entry && isStoredEngineUsable(stored, entry)) {\n return stored.engine;\n }\n }\n\n const detectedFromUser = await detectEngineFromUserSecrets();\n if (detectedFromUser) return detectedFromUser.name;\n\n return detectEngineFromEnv()?.name ?? null;\n } catch {\n return null;\n }\n}\n\nasync function queryRows<T extends Record<string, unknown>>(\n sql: string,\n args: unknown[] = [],\n): Promise<T[]> {\n try {\n const result = await getDbExec().execute({ sql, args });\n return result.rows as T[];\n } catch {\n return [];\n }\n}\n\nasync function initializeUsageMetricsTable(sinceMs: number): Promise<void> {\n try {\n // Initializes token_usage on fresh deployments before the read-only\n // aggregate queries below. The fake owner avoids changing visible data.\n await getUsageSummary({ ownerEmail: \"__dispatch_metrics_init__\", sinceMs });\n } catch {\n // Metrics should still render an empty state if usage storage is locked,\n // stale, or unavailable; each aggregate read below is already best-effort.\n }\n}\n\nasync function getViewerOrgRole(\n orgId: string | null,\n email: string,\n): Promise<string | null> {\n if (!orgId) return null;\n const rows = await queryRows<{ role?: string }>(\n `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n [orgId, email.toLowerCase()],\n );\n const role = rows[0]?.role;\n return typeof role === \"string\" ? role : null;\n}\n\nasync function listOrgMembers(orgId: string | null): Promise<MemberRecord[]> {\n if (!orgId) return [];\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT email, role, joined_at AS joined_at FROM org_members WHERE org_id = ? ORDER BY joined_at ASC`,\n [orgId],\n );\n return rows\n .map((row) => ({\n email: stringField(row, \"email\").trim(),\n role: stringField(row, \"role\") || null,\n joinedAt: nullableNumberField(row, \"joined_at\"),\n }))\n .filter((member) => member.email);\n}\n\nasync function listSignedInUsers(): Promise<MemberRecord[]> {\n const authRows = await queryRows<Record<string, unknown>>(\n `SELECT email, created_at AS joined_at FROM \"user\" ORDER BY created_at ASC`,\n );\n if (authRows.length > 0) {\n return authRows\n .map((row) => ({\n email: stringField(row, \"email\").trim(),\n role: null,\n joinedAt: nullableNumberField(row, \"joined_at\"),\n }))\n .filter((member) => member.email);\n }\n\n const usageRows = await queryRows<{ email?: string }>(\n `SELECT DISTINCT owner_email AS email FROM token_usage`,\n );\n const threadRows = await queryRows<{ email?: string }>(\n `SELECT DISTINCT owner_email AS email FROM chat_threads`,\n );\n const emails = new Set<string>();\n for (const row of [...usageRows, ...threadRows]) {\n if (row.email) emails.add(row.email);\n }\n return [...emails].sort().map((email) => ({\n email,\n role: null,\n joinedAt: null,\n }));\n}\n\nfunction usageScope(\n sinceMs: number,\n memberEmails: string[],\n): { where: string; args: unknown[] } {\n if (memberEmails.length === 0) {\n return { where: \"created_at >= ?\", args: [sinceMs] };\n }\n const placeholders = memberEmails.map(() => \"?\").join(\", \");\n return {\n where: `created_at >= ? AND owner_email IN (${placeholders})`,\n args: [sinceMs, ...memberEmails],\n };\n}\n\nfunction threadScope(\n sinceMs: number,\n memberEmails: string[],\n): { where: string; args: unknown[] } {\n if (memberEmails.length === 0) {\n return { where: \"updated_at >= ?\", args: [sinceMs] };\n }\n const placeholders = memberEmails.map(() => \"?\").join(\", \");\n return {\n where: `updated_at >= ? AND owner_email IN (${placeholders})`,\n args: [sinceMs, ...memberEmails],\n };\n}\n\nfunction bucketFromRow(row: Record<string, unknown>): UsageMetricBucket {\n const key = stringField(row, \"k\");\n return {\n key,\n label: labelForKey(key),\n costCents: numberField(row, \"cost_x100\") / 100,\n calls: numberField(row, \"calls\"),\n chatCalls: numberField(row, \"chat_calls\"),\n inputTokens: numberField(row, \"input_tokens\"),\n outputTokens: numberField(row, \"output_tokens\"),\n cacheReadTokens: numberField(row, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(row, \"cache_write_tokens\"),\n activeUsers: numberField(row, \"active_users\"),\n lastActiveAt: nullableNumberField(row, \"last_active_at\"),\n };\n}\n\nasync function usageBuckets(\n columnExpression: string,\n where: string,\n args: unknown[],\n limit: number,\n): Promise<UsageMetricBucket[]> {\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT ${columnExpression} AS k,\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100,\n COUNT(*) AS calls,\n SUM(CASE WHEN label = 'chat' THEN 1 ELSE 0 END) AS chat_calls,\n COALESCE(SUM(input_tokens), 0) AS input_tokens,\n COALESCE(SUM(output_tokens), 0) AS output_tokens,\n COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,\n COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,\n COUNT(DISTINCT owner_email) AS active_users,\n MAX(created_at) AS last_active_at\n FROM token_usage\n WHERE ${where}\n GROUP BY ${columnExpression}\n ORDER BY cost_x100 DESC\n LIMIT ?`,\n [...args, limit],\n );\n return rows.map(bucketFromRow);\n}\n\nasync function loadChatStats(\n where: string,\n args: unknown[],\n): Promise<Map<string, ChatStats>> {\n const rows = await queryRows<Record<string, unknown>>(\n `SELECT owner_email AS owner_email,\n COUNT(*) AS threads,\n COALESCE(SUM(message_count), 0) AS messages,\n MAX(updated_at) AS last_chat_at\n FROM chat_threads\n WHERE ${where}\n GROUP BY owner_email`,\n args,\n );\n return new Map(\n rows.map((row) => [\n stringField(row, \"owner_email\"),\n {\n threads: numberField(row, \"threads\"),\n messages: numberField(row, \"messages\"),\n lastChatAt: nullableNumberField(row, \"last_chat_at\"),\n },\n ]),\n );\n}\n\nasync function assertCanViewMetrics(): Promise<{\n viewerEmail: string;\n orgId: string | null;\n role: string | null;\n}> {\n const viewerEmail = currentOwnerEmail();\n const orgId = currentOrgId();\n const role = await getViewerOrgRole(orgId, viewerEmail);\n if (isEnvAdmin(viewerEmail) || role === \"owner\" || role === \"admin\") {\n return { viewerEmail, orgId, role };\n }\n if (!orgId) {\n return { viewerEmail, orgId, role };\n }\n throw new Error(\n \"Only organization owners and admins can view workspace usage metrics.\",\n );\n}\n\nexport async function listDispatchUsageMetrics(input: {\n sinceDays?: number;\n}): Promise<DispatchUsageMetrics> {\n const { viewerEmail, orgId, role } = await assertCanViewMetrics();\n const sinceDays = Math.max(1, Math.min(365, input.sinceDays ?? 30));\n const sinceMs = Date.now() - sinceDays * DAY_MS;\n const billing = usageBillingForEngine(await detectUsageEngineName());\n\n await initializeUsageMetricsTable(sinceMs);\n\n const rawMembers = orgId\n ? await listOrgMembers(orgId)\n : await listSignedInUsers();\n const members =\n orgId && rawMembers.length === 0\n ? [{ email: viewerEmail, role, joinedAt: null }]\n : rawMembers;\n const memberEmails = orgId ? members.map((member) => member.email) : [];\n const memberByEmail = new Map(\n members.map((member) => [member.email.toLowerCase(), member]),\n );\n const usage = usageScope(sinceMs, memberEmails);\n const threads = threadScope(sinceMs, memberEmails);\n\n const [apps, totalsRows, byApp, byUserBase, byLabel, byModel, chatStats] =\n await Promise.all([\n listWorkspaceApps({ includeAgentCards: false }),\n queryRows<Record<string, unknown>>(\n `SELECT\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100,\n COUNT(*) AS calls,\n SUM(CASE WHEN label = 'chat' THEN 1 ELSE 0 END) AS chat_calls,\n COALESCE(SUM(input_tokens), 0) AS input_tokens,\n COALESCE(SUM(output_tokens), 0) AS output_tokens,\n COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,\n COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,\n COUNT(DISTINCT owner_email) AS active_users\n FROM token_usage\n WHERE ${usage.where}`,\n usage.args,\n ),\n usageBuckets(\n `COALESCE(NULLIF(app, ''), 'unattributed')`,\n usage.where,\n usage.args,\n 20,\n ),\n usageBuckets(\"owner_email\", usage.where, usage.args, 50),\n usageBuckets(\n `COALESCE(NULLIF(label, ''), 'chat')`,\n usage.where,\n usage.args,\n 20,\n ),\n usageBuckets(\n `COALESCE(NULLIF(model, ''), 'unknown')`,\n usage.where,\n usage.args,\n 20,\n ),\n loadChatStats(threads.where, threads.args),\n ]);\n\n const topAppRows = await queryRows<Record<string, unknown>>(\n `SELECT owner_email AS owner_email,\n COALESCE(NULLIF(app, ''), 'unattributed') AS app,\n COALESCE(SUM(cost_cents_x100), 0) AS cost_x100\n FROM token_usage\n WHERE ${usage.where}\n GROUP BY owner_email, COALESCE(NULLIF(app, ''), 'unattributed')\n ORDER BY owner_email ASC, cost_x100 DESC`,\n usage.args,\n );\n const topAppByUser = new Map<string, string>();\n for (const row of topAppRows) {\n const email = stringField(row, \"owner_email\");\n if (!topAppByUser.has(email)) {\n topAppByUser.set(email, stringField(row, \"app\"));\n }\n }\n\n const byUserMap = new Map<string, UserUsageMetric>();\n for (const bucket of byUserBase) {\n const ownerEmail = bucket.key;\n const stats = chatStats.get(ownerEmail) ?? {\n threads: 0,\n messages: 0,\n lastChatAt: null,\n };\n const member = memberByEmail.get(ownerEmail.toLowerCase());\n byUserMap.set(ownerEmail, {\n ...bucket,\n ownerEmail,\n chatThreads: stats.threads,\n chatMessages: stats.messages,\n lastChatAt: stats.lastChatAt,\n topApp: topAppByUser.get(ownerEmail) ?? null,\n role: member?.role ?? null,\n });\n }\n for (const [ownerEmail, stats] of chatStats) {\n if (byUserMap.has(ownerEmail)) continue;\n const member = memberByEmail.get(ownerEmail.toLowerCase());\n byUserMap.set(ownerEmail, {\n key: ownerEmail,\n label: ownerEmail,\n ownerEmail,\n costCents: 0,\n calls: 0,\n chatCalls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n activeUsers: 1,\n lastActiveAt: stats.lastChatAt,\n chatThreads: stats.threads,\n chatMessages: stats.messages,\n lastChatAt: stats.lastChatAt,\n topApp: null,\n role: member?.role ?? null,\n });\n }\n\n const dayRows = await queryRows<Record<string, unknown>>(\n `SELECT created_at, owner_email, label, cost_cents_x100\n FROM token_usage\n WHERE ${usage.where}\n ORDER BY created_at ASC`,\n usage.args,\n );\n const dailyMap = new Map<\n string,\n { costX100: number; calls: number; chatCalls: number; users: Set<string> }\n >();\n for (const row of dayRows) {\n const date = new Date(numberField(row, \"created_at\"))\n .toISOString()\n .slice(0, 10);\n const current = dailyMap.get(date) ?? {\n costX100: 0,\n calls: 0,\n chatCalls: 0,\n users: new Set<string>(),\n };\n current.costX100 += numberField(row, \"cost_cents_x100\");\n current.calls += 1;\n if (stringField(row, \"label\") === \"chat\") current.chatCalls += 1;\n current.users.add(stringField(row, \"owner_email\"));\n dailyMap.set(date, current);\n }\n const daily = [...dailyMap.entries()]\n .map(([date, value]) => ({\n date,\n costCents: value.costX100 / 100,\n calls: value.calls,\n chatCalls: value.chatCalls,\n activeUsers: value.users.size,\n }))\n .sort((a, b) => a.date.localeCompare(b.date));\n\n const recentRows = await queryRows<Record<string, unknown>>(\n `SELECT id, created_at, owner_email, app, label, model,\n input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,\n cost_cents_x100\n FROM token_usage\n WHERE ${usage.where}\n ORDER BY created_at DESC\n LIMIT 50`,\n usage.args,\n );\n const recent = recentRows.map((row) => ({\n id: numberField(row, \"id\"),\n createdAt: numberField(row, \"created_at\"),\n ownerEmail: stringField(row, \"owner_email\"),\n app: stringField(row, \"app\") || \"unattributed\",\n label: stringField(row, \"label\") || \"chat\",\n model: stringField(row, \"model\") || \"unknown\",\n inputTokens: numberField(row, \"input_tokens\"),\n outputTokens: numberField(row, \"output_tokens\"),\n cacheReadTokens: numberField(row, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(row, \"cache_write_tokens\"),\n costCents: numberField(row, \"cost_cents_x100\") / 100,\n }));\n\n const appUsageByKey = new Map(\n byApp.map((bucket) => [normalizeAppKey(bucket.key), bucket]),\n );\n const accessUsers = members.length || byUserMap.size;\n const accessModel = orgId ? \"workspace\" : \"solo\";\n const accessLabel = orgId ? \"Workspace members\" : \"Signed-in users\";\n const appAccess = apps.map((app) => {\n const usageBucket = appUsageByKey.get(normalizeAppKey(app.id));\n return {\n id: app.id,\n name: app.name,\n path: app.path,\n status: app.status,\n statusLabel: app.statusLabel,\n isDispatch: app.isDispatch,\n accessModel,\n accessLabel,\n accessUsers,\n usersWithUsage: usageBucket?.activeUsers ?? 0,\n usageCalls: usageBucket?.calls ?? 0,\n chatCalls: usageBucket?.chatCalls ?? 0,\n costCents: usageBucket?.costCents ?? 0,\n lastActiveAt: usageBucket?.lastActiveAt ?? null,\n } satisfies AppAccessMetric;\n });\n\n const totals = totalsRows[0] ?? {};\n const chatThreadTotals = [...chatStats.values()].reduce(\n (acc, value) => ({\n threads: acc.threads + value.threads,\n messages: acc.messages + value.messages,\n }),\n { threads: 0, messages: 0 },\n );\n\n return {\n billing,\n sinceMs,\n sinceDays,\n generatedAt: Date.now(),\n access: {\n viewerEmail,\n orgId,\n role,\n scope: orgId ? \"organization\" : \"solo\",\n totalUsers: accessUsers,\n },\n totals: {\n costCents: numberField(totals, \"cost_x100\") / 100,\n calls: numberField(totals, \"calls\"),\n chatCalls: numberField(totals, \"chat_calls\"),\n inputTokens: numberField(totals, \"input_tokens\"),\n outputTokens: numberField(totals, \"output_tokens\"),\n cacheReadTokens: numberField(totals, \"cache_read_tokens\"),\n cacheWriteTokens: numberField(totals, \"cache_write_tokens\"),\n activeUsers: numberField(totals, \"active_users\"),\n chatThreads: chatThreadTotals.threads,\n chatMessages: chatThreadTotals.messages,\n workspaceApps: apps.filter((app) => !app.isDispatch).length,\n },\n byApp,\n byUser: [...byUserMap.values()].sort((a, b) => {\n if (b.costCents !== a.costCents) return b.costCents - a.costCents;\n return (b.lastActiveAt ?? 0) - (a.lastActiveAt ?? 0);\n }),\n byLabel,\n byModel,\n daily,\n appAccess,\n recent,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.13.11",
3
+ "version": "0.13.13",
4
4
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -94,7 +94,7 @@
94
94
  "typescript-7": "npm:typescript@^7.0.2",
95
95
  "vite": "8.1.0",
96
96
  "vitest": "^4.1.5",
97
- "@agent-native/core": "0.91.2"
97
+ "@agent-native/core": "0.92.1"
98
98
  },
99
99
  "peerDependencies": {
100
100
  "@agent-native/core": ">=0.8.0",
@@ -211,11 +211,7 @@ function AppsPanel({
211
211
  export function DispatchControlPlane() {
212
212
  const { data: workspaceApps = [], isLoading: appsLoading } = useActionQuery<
213
213
  WorkspaceAppSummary[]
214
- >(
215
- "list-workspace-apps",
216
- { includeAgentCards: false, includeArchived: true },
217
- { refetchInterval: 2_000 },
218
- );
214
+ >("list-workspace-apps", { includeAgentCards: false, includeArchived: true });
219
215
 
220
216
  return (
221
217
  <DispatchShell
@@ -62,7 +62,6 @@ export function useNavigationState(extensions?: DispatchExtensionConfig) {
62
62
  }
63
63
  return null;
64
64
  },
65
- refetchInterval: 2_000,
66
65
  structuralSharing: false,
67
66
  });
68
67
 
@@ -275,7 +274,7 @@ function resolvePath(
275
274
  case "threads":
276
275
  return "/thread-debug";
277
276
  case "team":
278
- return "/settings#team";
277
+ return "/settings#organization";
279
278
  case "extensions":
280
279
  return command?.extensionId
281
280
  ? `/extensions/${encodeURIComponent(command.extensionId)}`
@@ -96,11 +96,9 @@ export async function clientLoader({
96
96
  export default function WorkspaceAppCatchAllRoute() {
97
97
  const t = useT();
98
98
  const { appId } = useParams();
99
- const { data: apps = [], isLoading } = useActionQuery(
100
- "list-workspace-apps",
101
- { includeAgentCards: false },
102
- { refetchInterval: 2_000 },
103
- );
99
+ const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", {
100
+ includeAgentCards: false,
101
+ });
104
102
  const app = useMemo(
105
103
  () =>
106
104
  (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,
@@ -23,13 +23,9 @@ export function meta() {
23
23
  export default function WorkspaceAppRoute() {
24
24
  const t = useT();
25
25
  const { appId } = useParams();
26
- const { data: apps = [], isLoading } = useActionQuery(
27
- "list-workspace-apps",
28
- { includeAgentCards: false },
29
- {
30
- refetchInterval: 2_000,
31
- },
32
- );
26
+ const { data: apps = [], isLoading } = useActionQuery("list-workspace-apps", {
27
+ includeAgentCards: false,
28
+ });
33
29
  const app = useMemo(
34
30
  () =>
35
31
  (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,
@@ -78,9 +78,6 @@ export default function AppsRoute() {
78
78
  const { data: apps = [], isLoading: appsLoading } = useActionQuery(
79
79
  "list-workspace-apps",
80
80
  { includeAgentCards: false, includeArchived: true },
81
- {
82
- refetchInterval: 2_000,
83
- },
84
81
  );
85
82
  const { data: workspace } = useActionQuery(
86
83
  "get-workspace-info",
@@ -5,5 +5,5 @@ export function meta() {
5
5
  }
6
6
 
7
7
  export default function TeamRoute() {
8
- return <Navigate to="/settings#team" replace />;
8
+ return <Navigate to="/settings#organization" replace />;
9
9
  }
@@ -0,0 +1,102 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => ({
4
+ detectEngineFromEnv: vi.fn(() => null),
5
+ detectEngineFromUserSecrets: vi.fn(async () => null),
6
+ execute: vi.fn(),
7
+ getSetting: vi.fn(async () => null),
8
+ getUsageSummary: vi.fn(),
9
+ listWorkspaceApps: vi.fn(),
10
+ registerBuiltinEngines: vi.fn(),
11
+ }));
12
+
13
+ vi.mock("@agent-native/core/agent/engine", () => ({
14
+ detectEngineFromEnv: (...args: any[]) => mocks.detectEngineFromEnv(...args),
15
+ detectEngineFromUserSecrets: (...args: any[]) =>
16
+ mocks.detectEngineFromUserSecrets(...args),
17
+ getAgentEngineEntry: vi.fn(() => null),
18
+ isAgentEngineSettingConfigured: vi.fn(() => false),
19
+ isStoredEngineUsable: vi.fn(() => false),
20
+ registerBuiltinEngines: () => mocks.registerBuiltinEngines(),
21
+ }));
22
+
23
+ vi.mock("@agent-native/core/db", () => ({
24
+ getDbExec: () => ({
25
+ execute: (...args: any[]) => mocks.execute(...args),
26
+ }),
27
+ }));
28
+
29
+ vi.mock("@agent-native/core/settings", () => ({
30
+ getSetting: (...args: any[]) => mocks.getSetting(...args),
31
+ }));
32
+
33
+ vi.mock("@agent-native/core/usage", () => ({
34
+ getUsageSummary: (...args: any[]) => mocks.getUsageSummary(...args),
35
+ usageBillingForEngine: () => ({
36
+ unit: "usd",
37
+ label: "Estimated spend",
38
+ shortLabel: "Cost",
39
+ source: "estimated-provider-cost",
40
+ }),
41
+ }));
42
+
43
+ vi.mock("./app-creation-store.js", () => ({
44
+ listWorkspaceApps: (...args: any[]) => mocks.listWorkspaceApps(...args),
45
+ }));
46
+
47
+ vi.mock("./dispatch-store.js", () => ({
48
+ currentOrgId: () => null,
49
+ currentOwnerEmail: () => "owner@example.test",
50
+ }));
51
+
52
+ const { listDispatchUsageMetrics } = await import("./usage-metrics-store.js");
53
+
54
+ afterEach(() => {
55
+ vi.clearAllMocks();
56
+ });
57
+
58
+ describe("listDispatchUsageMetrics", () => {
59
+ it("returns empty metrics when usage storage bootstrap and reads fail", async () => {
60
+ mocks.getUsageSummary.mockRejectedValue(new Error("database is locked"));
61
+ mocks.execute.mockRejectedValue(new Error("no such table: token_usage"));
62
+ mocks.listWorkspaceApps.mockResolvedValue([
63
+ {
64
+ id: "dispatch",
65
+ name: "Dispatch",
66
+ path: "/dispatch",
67
+ status: "ready",
68
+ isDispatch: true,
69
+ },
70
+ ]);
71
+
72
+ const metrics = await listDispatchUsageMetrics({ sinceDays: 30 });
73
+
74
+ expect(mocks.getUsageSummary).toHaveBeenCalledWith({
75
+ ownerEmail: "__dispatch_metrics_init__",
76
+ sinceMs: expect.any(Number),
77
+ });
78
+ expect(metrics.access).toEqual({
79
+ viewerEmail: "owner@example.test",
80
+ orgId: null,
81
+ role: null,
82
+ scope: "solo",
83
+ totalUsers: 0,
84
+ });
85
+ expect(metrics.totals).toEqual({
86
+ costCents: 0,
87
+ calls: 0,
88
+ chatCalls: 0,
89
+ inputTokens: 0,
90
+ outputTokens: 0,
91
+ cacheReadTokens: 0,
92
+ cacheWriteTokens: 0,
93
+ activeUsers: 0,
94
+ chatThreads: 0,
95
+ chatMessages: 0,
96
+ workspaceApps: 0,
97
+ });
98
+ expect(metrics.byUser).toEqual([]);
99
+ expect(metrics.recent).toEqual([]);
100
+ expect(metrics.appAccess).toHaveLength(1);
101
+ });
102
+ });
@@ -213,6 +213,17 @@ async function queryRows<T extends Record<string, unknown>>(
213
213
  }
214
214
  }
215
215
 
216
+ async function initializeUsageMetricsTable(sinceMs: number): Promise<void> {
217
+ try {
218
+ // Initializes token_usage on fresh deployments before the read-only
219
+ // aggregate queries below. The fake owner avoids changing visible data.
220
+ await getUsageSummary({ ownerEmail: "__dispatch_metrics_init__", sinceMs });
221
+ } catch {
222
+ // Metrics should still render an empty state if usage storage is locked,
223
+ // stale, or unavailable; each aggregate read below is already best-effort.
224
+ }
225
+ }
226
+
216
227
  async function getViewerOrgRole(
217
228
  orgId: string | null,
218
229
  email: string,
@@ -397,9 +408,7 @@ export async function listDispatchUsageMetrics(input: {
397
408
  const sinceMs = Date.now() - sinceDays * DAY_MS;
398
409
  const billing = usageBillingForEngine(await detectUsageEngineName());
399
410
 
400
- // Initializes token_usage on fresh deployments before the read-only
401
- // aggregate queries below. The fake owner avoids changing visible data.
402
- await getUsageSummary({ ownerEmail: "__dispatch_metrics_init__", sinceMs });
411
+ await initializeUsageMetricsTable(sinceMs);
403
412
 
404
413
  const rawMembers = orgId
405
414
  ? await listOrgMembers(orgId)