@agent-native/dispatch 0.1.1 → 0.2.3

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.
Files changed (146) hide show
  1. package/dist/actions/index.d.ts.map +1 -1
  2. package/dist/actions/index.js +2 -0
  3. package/dist/actions/index.js.map +1 -1
  4. package/dist/actions/list-dispatch-usage-metrics.d.ts +3 -0
  5. package/dist/actions/list-dispatch-usage-metrics.d.ts.map +1 -0
  6. package/dist/actions/list-dispatch-usage-metrics.js +18 -0
  7. package/dist/actions/list-dispatch-usage-metrics.js.map +1 -0
  8. package/dist/actions/navigate.d.ts +1 -0
  9. package/dist/actions/navigate.d.ts.map +1 -1
  10. package/dist/actions/navigate.js +3 -17
  11. package/dist/actions/navigate.js.map +1 -1
  12. package/dist/actions/view-screen.d.ts.map +1 -1
  13. package/dist/actions/view-screen.js +19 -0
  14. package/dist/actions/view-screen.js.map +1 -1
  15. package/dist/components/agents-panel.js +3 -3
  16. package/dist/components/app-keys-popover.js +2 -2
  17. package/dist/components/create-app-popover.js +2 -2
  18. package/dist/components/dispatch-shell.js +2 -2
  19. package/dist/components/index.d.ts +1 -0
  20. package/dist/components/index.d.ts.map +1 -1
  21. package/dist/components/index.js.map +1 -1
  22. package/dist/components/layout/Header.js +5 -5
  23. package/dist/components/layout/Header.js.map +1 -1
  24. package/dist/components/layout/Layout.d.ts +28 -3
  25. package/dist/components/layout/Layout.d.ts.map +1 -1
  26. package/dist/components/layout/Layout.js +138 -28
  27. package/dist/components/layout/Layout.js.map +1 -1
  28. package/dist/components/messaging-setup-panel.js +4 -4
  29. package/dist/components/ui/accordion.js +1 -1
  30. package/dist/components/ui/alert-dialog.js +2 -2
  31. package/dist/components/ui/alert.js +1 -1
  32. package/dist/components/ui/avatar.js +1 -1
  33. package/dist/components/ui/badge.js +1 -1
  34. package/dist/components/ui/breadcrumb.js +1 -1
  35. package/dist/components/ui/button.js +1 -1
  36. package/dist/components/ui/calendar.js +2 -2
  37. package/dist/components/ui/card.js +1 -1
  38. package/dist/components/ui/carousel.d.ts +2 -2
  39. package/dist/components/ui/carousel.js +2 -2
  40. package/dist/components/ui/chart.js +1 -1
  41. package/dist/components/ui/checkbox.js +1 -1
  42. package/dist/components/ui/command.js +2 -2
  43. package/dist/components/ui/context-menu.js +1 -1
  44. package/dist/components/ui/dialog.js +1 -1
  45. package/dist/components/ui/drawer.js +1 -1
  46. package/dist/components/ui/dropdown-menu.js +1 -1
  47. package/dist/components/ui/form.js +2 -2
  48. package/dist/components/ui/hover-card.js +1 -1
  49. package/dist/components/ui/input-otp.js +1 -1
  50. package/dist/components/ui/input.js +1 -1
  51. package/dist/components/ui/label.js +1 -1
  52. package/dist/components/ui/menubar.js +1 -1
  53. package/dist/components/ui/navigation-menu.js +1 -1
  54. package/dist/components/ui/pagination.d.ts +1 -1
  55. package/dist/components/ui/pagination.js +2 -2
  56. package/dist/components/ui/popover.js +1 -1
  57. package/dist/components/ui/progress.js +1 -1
  58. package/dist/components/ui/radio-group.js +1 -1
  59. package/dist/components/ui/resizable.js +1 -1
  60. package/dist/components/ui/scroll-area.js +1 -1
  61. package/dist/components/ui/select.js +1 -1
  62. package/dist/components/ui/separator.js +1 -1
  63. package/dist/components/ui/sheet.js +1 -1
  64. package/dist/components/ui/sidebar.d.ts +2 -2
  65. package/dist/components/ui/sidebar.d.ts.map +1 -1
  66. package/dist/components/ui/sidebar.js +9 -9
  67. package/dist/components/ui/sidebar.js.map +1 -1
  68. package/dist/components/ui/skeleton.js +1 -1
  69. package/dist/components/ui/slider.js +1 -1
  70. package/dist/components/ui/sonner.js +1 -1
  71. package/dist/components/ui/spinner.js +1 -1
  72. package/dist/components/ui/switch.js +1 -1
  73. package/dist/components/ui/table.js +1 -1
  74. package/dist/components/ui/tabs.js +1 -1
  75. package/dist/components/ui/textarea.js +1 -1
  76. package/dist/components/ui/toast.js +1 -1
  77. package/dist/components/ui/toaster.js +2 -2
  78. package/dist/components/ui/toggle-group.js +2 -2
  79. package/dist/components/ui/toggle.js +1 -1
  80. package/dist/components/ui/tooltip.js +1 -1
  81. package/dist/components/ui/use-toast.d.ts +1 -1
  82. package/dist/components/ui/use-toast.js +1 -1
  83. package/dist/hooks/use-navigation-state.d.ts +2 -1
  84. package/dist/hooks/use-navigation-state.d.ts.map +1 -1
  85. package/dist/hooks/use-navigation-state.js +36 -8
  86. package/dist/hooks/use-navigation-state.js.map +1 -1
  87. package/dist/hooks/use-toast.d.ts +1 -1
  88. package/dist/routes/index.d.ts.map +1 -1
  89. package/dist/routes/index.js +3 -2
  90. package/dist/routes/index.js.map +1 -1
  91. package/dist/routes/pages/_index.js +1 -1
  92. package/dist/routes/pages/agents.js +2 -2
  93. package/dist/routes/pages/approval.js +2 -2
  94. package/dist/routes/pages/approvals.js +4 -4
  95. package/dist/routes/pages/apps.$appId.js +3 -3
  96. package/dist/routes/pages/apps.js +5 -5
  97. package/dist/routes/pages/audit.js +1 -1
  98. package/dist/routes/pages/destinations.js +6 -6
  99. package/dist/routes/pages/extensions.$id.d.ts +2 -0
  100. package/dist/routes/pages/extensions.$id.d.ts.map +1 -0
  101. package/dist/routes/pages/extensions.$id.js +6 -0
  102. package/dist/routes/pages/extensions.$id.js.map +1 -0
  103. package/dist/routes/pages/extensions._index.d.ts +2 -0
  104. package/dist/routes/pages/extensions._index.d.ts.map +1 -0
  105. package/dist/routes/pages/extensions._index.js +6 -0
  106. package/dist/routes/pages/extensions._index.js.map +1 -0
  107. package/dist/routes/pages/identities.js +2 -2
  108. package/dist/routes/pages/integrations.js +4 -4
  109. package/dist/routes/pages/messaging.js +2 -2
  110. package/dist/routes/pages/metrics.d.ts +5 -0
  111. package/dist/routes/pages/metrics.d.ts.map +1 -0
  112. package/dist/routes/pages/metrics.js +135 -0
  113. package/dist/routes/pages/metrics.js.map +1 -0
  114. package/dist/routes/pages/new-app.js +1 -1
  115. package/dist/routes/pages/overview.d.ts.map +1 -1
  116. package/dist/routes/pages/overview.js +9 -17
  117. package/dist/routes/pages/overview.js.map +1 -1
  118. package/dist/routes/pages/team.js +1 -1
  119. package/dist/routes/pages/vault.js +10 -10
  120. package/dist/routes/pages/workspace.js +10 -10
  121. package/dist/server/lib/pre-auth-routing.d.ts.map +1 -1
  122. package/dist/server/lib/pre-auth-routing.js +9 -2
  123. package/dist/server/lib/pre-auth-routing.js.map +1 -1
  124. package/dist/server/lib/usage-metrics-store.d.ts +93 -0
  125. package/dist/server/lib/usage-metrics-store.d.ts.map +1 -0
  126. package/dist/server/lib/usage-metrics-store.js +386 -0
  127. package/dist/server/lib/usage-metrics-store.js.map +1 -0
  128. package/package.json +11 -6
  129. package/src/actions/index.ts +2 -0
  130. package/src/actions/list-dispatch-usage-metrics.ts +19 -0
  131. package/src/actions/navigate.ts +5 -17
  132. package/src/actions/view-screen.ts +18 -0
  133. package/src/components/index.ts +6 -0
  134. package/src/components/layout/Header.tsx +2 -2
  135. package/src/components/layout/Layout.tsx +197 -48
  136. package/src/components/ui/sidebar.tsx +22 -18
  137. package/src/hooks/use-navigation-state.ts +57 -8
  138. package/src/routes/index.ts +3 -2
  139. package/src/routes/pages/extensions.$id.tsx +5 -0
  140. package/src/routes/pages/extensions._index.tsx +5 -0
  141. package/src/routes/pages/metrics.tsx +667 -0
  142. package/src/routes/pages/overview.tsx +0 -10
  143. package/src/server/lib/pre-auth-routing.ts +10 -2
  144. package/src/server/lib/usage-metrics-store.ts +605 -0
  145. package/src/styles/dispatch-css.spec.ts +55 -0
  146. package/src/styles/dispatch.css +9 -0
@@ -2,14 +2,14 @@ import { useEffect } from "react";
2
2
  import { useLocation, useNavigate } from "react-router";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { agentNativePath, appBasePath, appPath, } from "@agent-native/core/client";
5
- export function useNavigationState() {
5
+ export function useNavigationState(extensions) {
6
6
  const location = useLocation();
7
7
  const navigate = useNavigate();
8
8
  const qc = useQueryClient();
9
9
  // Sync current route to application state
10
10
  useEffect(() => {
11
11
  const state = {
12
- view: resolveView(location.pathname),
12
+ view: resolveView(location.pathname, extensions),
13
13
  path: appPath(location.pathname),
14
14
  };
15
15
  fetch(agentNativePath("/_agent-native/application-state/navigation"), {
@@ -18,7 +18,7 @@ export function useNavigationState() {
18
18
  headers: { "Content-Type": "application/json" },
19
19
  body: JSON.stringify(state),
20
20
  }).catch(() => { });
21
- }, [location.pathname]);
21
+ }, [extensions, location.pathname]);
22
22
  // Listen for navigate commands from agent
23
23
  const { data: navCommand } = useQuery({
24
24
  queryKey: ["navigate-command"],
@@ -47,10 +47,10 @@ export function useNavigationState() {
47
47
  }).catch(() => { });
48
48
  const cmd = navCommand;
49
49
  // Navigate to a specific path or resolve view name to path
50
- const path = routerPath(cmd.path || resolvePath(cmd.view) || "/overview");
50
+ const path = routerPath(cmd.path || resolvePath(cmd.view, extensions) || "/overview");
51
51
  navigate(path);
52
52
  qc.setQueryData(["navigate-command"], null);
53
- }, [navCommand, navigate, qc]);
53
+ }, [extensions, navCommand, navigate, qc]);
54
54
  }
55
55
  function routerPath(path) {
56
56
  const basePath = appBasePath();
@@ -63,9 +63,34 @@ function routerPath(path) {
63
63
  }
64
64
  return path;
65
65
  }
66
- function resolveView(pathname) {
66
+ function extensionItemMatchesPath(item, pathname) {
67
+ if (item.match) {
68
+ try {
69
+ if (item.match(pathname))
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ return pathname === item.to || pathname.startsWith(`${item.to}/`);
77
+ }
78
+ function resolveExtensionView(pathname, extensions) {
79
+ return extensions?.navItems?.find((item) => extensionItemMatchesPath(item, pathname))?.id;
80
+ }
81
+ function resolveExtensionPath(view, extensions) {
82
+ if (!view)
83
+ return undefined;
84
+ return extensions?.navItems?.find((item) => item.id === view)?.to;
85
+ }
86
+ function resolveView(pathname, extensions) {
87
+ const extensionView = resolveExtensionView(pathname, extensions);
88
+ if (extensionView)
89
+ return extensionView;
67
90
  if (pathname.startsWith("/apps"))
68
91
  return "apps";
92
+ if (pathname.startsWith("/metrics"))
93
+ return "metrics";
69
94
  if (pathname.startsWith("/new-app"))
70
95
  return "new-app";
71
96
  if (pathname.startsWith("/vault"))
@@ -90,12 +115,15 @@ function resolveView(pathname) {
90
115
  return "team";
91
116
  return "overview";
92
117
  }
93
- function resolvePath(view) {
118
+ function resolvePath(view, extensions) {
94
119
  switch (view) {
95
120
  case "overview":
96
121
  return "/overview";
97
122
  case "apps":
98
123
  return "/apps";
124
+ case "metrics":
125
+ case "usage":
126
+ return "/metrics";
99
127
  case "new-app":
100
128
  case "create-app":
101
129
  return "/new-app";
@@ -123,7 +151,7 @@ function resolvePath(view) {
123
151
  case "team":
124
152
  return "/team";
125
153
  default:
126
- return undefined;
154
+ return resolveExtensionPath(view, extensions);
127
155
  }
128
156
  }
129
157
  //# sourceMappingURL=use-navigation-state.js.map
@@ -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 { useEffect } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport {\n agentNativePath,\n appBasePath,\n appPath,\n} from \"@agent-native/core/client\";\n\nexport interface NavigationState {\n view: string;\n path?: string;\n}\n\nexport function useNavigationState() {\n const location = useLocation();\n const navigate = useNavigate();\n const qc = useQueryClient();\n\n // Sync current route to application state\n useEffect(() => {\n const state: NavigationState = {\n view: resolveView(location.pathname),\n path: appPath(location.pathname),\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 }, [location.pathname]);\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 refetchIntervalInBackground: true,\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 path = routerPath(cmd.path || resolvePath(cmd.view) || \"/overview\");\n navigate(path);\n qc.setQueryData([\"navigate-command\"], null);\n }, [navCommand, navigate, qc]);\n}\n\nfunction routerPath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n if (path === basePath) return \"/\";\n if (path.startsWith(`${basePath}/`)) {\n return path.slice(basePath.length) || \"/\";\n }\n return path;\n}\n\nfunction resolveView(pathname: string): string {\n if (pathname.startsWith(\"/apps\")) return \"apps\";\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(\"/audit\")) return \"audit\";\n if (pathname.startsWith(\"/team\")) return \"team\";\n return \"overview\";\n}\n\nfunction resolvePath(view?: string): string | undefined {\n switch (view) {\n case \"overview\":\n return \"/overview\";\n case \"apps\":\n return \"/apps\";\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 \"audit\":\n return \"/audit\";\n case \"team\":\n return \"/team\";\n default:\n return undefined;\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 { useEffect } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport {\n agentNativePath,\n appBasePath,\n appPath,\n} from \"@agent-native/core/client\";\nimport type {\n DispatchExtensionConfig,\n DispatchNavItem,\n} from \"../components/index.js\";\n\nexport interface NavigationState {\n view: string;\n path?: 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 state: NavigationState = {\n view: resolveView(location.pathname, extensions),\n path: appPath(location.pathname),\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]);\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 refetchIntervalInBackground: true,\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 path = routerPath(\n cmd.path || resolvePath(cmd.view, extensions) || \"/overview\",\n );\n navigate(path);\n qc.setQueryData([\"navigate-command\"], null);\n }, [extensions, navCommand, navigate, qc]);\n}\n\nfunction routerPath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n if (path === basePath) return \"/\";\n if (path.startsWith(`${basePath}/`)) {\n return path.slice(basePath.length) || \"/\";\n }\n return path;\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.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(\"/audit\")) return \"audit\";\n if (pathname.startsWith(\"/team\")) return \"team\";\n return \"overview\";\n}\n\nfunction resolvePath(\n view?: string,\n extensions?: DispatchExtensionConfig,\n): string | undefined {\n switch (view) {\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 \"audit\":\n return \"/audit\";\n case \"team\":\n return \"/team\";\n default:\n return resolveExtensionPath(view, extensions);\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import type { ToastActionElement, ToastProps } from "../components/ui/toast";
2
+ import type { ToastActionElement, ToastProps } from "../components/ui/toast.js";
3
3
  type ToasterToast = ToastProps & {
4
4
  id: string;
5
5
  title?: React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,cAAc,EAAE,WAmB5B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,cAAc,EAAE,WAoB5B,CAAC"}
@@ -31,6 +31,7 @@ import { route, index } from "@react-router/dev/routes";
31
31
  export const dispatchRoutes = [
32
32
  index("./pages/_index.js"),
33
33
  route("overview", "./pages/overview.js"),
34
+ route("metrics", "./pages/metrics.js"),
34
35
  route("apps", "./pages/apps.js"),
35
36
  route("apps/:appId", "./pages/apps.$appId.js"),
36
37
  route("new-app", "./pages/new-app.js"),
@@ -45,7 +46,7 @@ export const dispatchRoutes = [
45
46
  route("approvals", "./pages/approvals.js"),
46
47
  route("audit", "./pages/audit.js"),
47
48
  route("team", "./pages/team.js"),
48
- route("tools", "./pages/tools._index.js"),
49
- route("tools/:id", "./pages/tools.$id.js"),
49
+ route("extensions", "./pages/extensions._index.js"),
50
+ route("extensions/:id", "./pages/extensions.$id.js"),
50
51
  ];
51
52
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,KAAK,CAAC,mBAAmB,CAAC;IAC1B,KAAK,CAAC,UAAU,EAAE,qBAAqB,CAAC;IACxC,KAAK,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAChC,KAAK,CAAC,aAAa,EAAE,wBAAwB,CAAC;IAC9C,KAAK,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACtC,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAClC,KAAK,CAAC,cAAc,EAAE,yBAAyB,CAAC;IAChD,KAAK,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACpC,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,cAAc,EAAE,yBAAyB,CAAC;IAChD,KAAK,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC5C,KAAK,CAAC,UAAU,EAAE,qBAAqB,CAAC;IACxC,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAClC,KAAK,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAChC,KAAK,CAAC,OAAO,EAAE,yBAAyB,CAAC;IACzC,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;CAC3C,CAAC","sourcesContent":["import { type RouteConfig, route, index } from \"@react-router/dev/routes\";\n\n/**\n * Dispatch's routes as a programmatic `RouteConfig[]`. Splat into the\n * consumer's `app/routes.ts`:\n *\n * ```ts\n * import { type RouteConfig } from \"@react-router/dev/routes\";\n * import { dispatchRoutes } from \"@agent-native/dispatch/routes\";\n *\n * export default [\n * ...localRoutes, // consumer's own routes win on collision\n * ...dispatchRoutes, // dispatch fills in everything else\n * ] satisfies RouteConfig;\n * ```\n *\n * Route precedence: React Router 7 matches in declaration order, so\n * placing `dispatchRoutes` LAST means consumer-defined routes with the\n * same path take precedence. To override a single dispatch route, define\n * it in your local routes; to keep it, omit it.\n *\n * The `file` paths below resolve relative to this file at runtime — they\n * point into `packages/dispatch/dist/routes/pages/*.js` after build.\n *\n * Naming maps the original flatRoutes file conventions:\n * `_index.tsx` → `index(...)`\n * `<name>.tsx` → `route(\"<name>\", ...)`\n * `<a>.$<param>.tsx` → `route(\"<a>/:<param>\", ...)`\n * `<a>._index.tsx` → flattened as `route(\"<a>\", ...)` (workspace\n * versions are bare and don't wrap a parent layout)\n */\nexport const dispatchRoutes: RouteConfig = [\n index(\"./pages/_index.js\"),\n route(\"overview\", \"./pages/overview.js\"),\n route(\"apps\", \"./pages/apps.js\"),\n route(\"apps/:appId\", \"./pages/apps.$appId.js\"),\n route(\"new-app\", \"./pages/new-app.js\"),\n route(\"vault\", \"./pages/vault.js\"),\n route(\"integrations\", \"./pages/integrations.js\"),\n route(\"agents\", \"./pages/agents.js\"),\n route(\"workspace\", \"./pages/workspace.js\"),\n route(\"messaging\", \"./pages/messaging.js\"),\n route(\"destinations\", \"./pages/destinations.js\"),\n route(\"identities\", \"./pages/identities.js\"),\n route(\"approval\", \"./pages/approval.js\"),\n route(\"approvals\", \"./pages/approvals.js\"),\n route(\"audit\", \"./pages/audit.js\"),\n route(\"team\", \"./pages/team.js\"),\n route(\"tools\", \"./pages/tools._index.js\"),\n route(\"tools/:id\", \"./pages/tools.$id.js\"),\n];\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,KAAK,CAAC,mBAAmB,CAAC;IAC1B,KAAK,CAAC,UAAU,EAAE,qBAAqB,CAAC;IACxC,KAAK,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACtC,KAAK,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAChC,KAAK,CAAC,aAAa,EAAE,wBAAwB,CAAC;IAC9C,KAAK,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACtC,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAClC,KAAK,CAAC,cAAc,EAAE,yBAAyB,CAAC;IAChD,KAAK,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACpC,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,cAAc,EAAE,yBAAyB,CAAC;IAChD,KAAK,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC5C,KAAK,CAAC,UAAU,EAAE,qBAAqB,CAAC;IACxC,KAAK,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC1C,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAClC,KAAK,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAChC,KAAK,CAAC,YAAY,EAAE,8BAA8B,CAAC;IACnD,KAAK,CAAC,gBAAgB,EAAE,2BAA2B,CAAC;CACrD,CAAC","sourcesContent":["import { type RouteConfig, route, index } from \"@react-router/dev/routes\";\n\n/**\n * Dispatch's routes as a programmatic `RouteConfig[]`. Splat into the\n * consumer's `app/routes.ts`:\n *\n * ```ts\n * import { type RouteConfig } from \"@react-router/dev/routes\";\n * import { dispatchRoutes } from \"@agent-native/dispatch/routes\";\n *\n * export default [\n * ...localRoutes, // consumer's own routes win on collision\n * ...dispatchRoutes, // dispatch fills in everything else\n * ] satisfies RouteConfig;\n * ```\n *\n * Route precedence: React Router 7 matches in declaration order, so\n * placing `dispatchRoutes` LAST means consumer-defined routes with the\n * same path take precedence. To override a single dispatch route, define\n * it in your local routes; to keep it, omit it.\n *\n * The `file` paths below resolve relative to this file at runtime — they\n * point into `packages/dispatch/dist/routes/pages/*.js` after build.\n *\n * Naming maps the original flatRoutes file conventions:\n * `_index.tsx` → `index(...)`\n * `<name>.tsx` → `route(\"<name>\", ...)`\n * `<a>.$<param>.tsx` → `route(\"<a>/:<param>\", ...)`\n * `<a>._index.tsx` → flattened as `route(\"<a>\", ...)` (workspace\n * versions are bare and don't wrap a parent layout)\n */\nexport const dispatchRoutes: RouteConfig = [\n index(\"./pages/_index.js\"),\n route(\"overview\", \"./pages/overview.js\"),\n route(\"metrics\", \"./pages/metrics.js\"),\n route(\"apps\", \"./pages/apps.js\"),\n route(\"apps/:appId\", \"./pages/apps.$appId.js\"),\n route(\"new-app\", \"./pages/new-app.js\"),\n route(\"vault\", \"./pages/vault.js\"),\n route(\"integrations\", \"./pages/integrations.js\"),\n route(\"agents\", \"./pages/agents.js\"),\n route(\"workspace\", \"./pages/workspace.js\"),\n route(\"messaging\", \"./pages/messaging.js\"),\n route(\"destinations\", \"./pages/destinations.js\"),\n route(\"identities\", \"./pages/identities.js\"),\n route(\"approval\", \"./pages/approval.js\"),\n route(\"approvals\", \"./pages/approvals.js\"),\n route(\"audit\", \"./pages/audit.js\"),\n route(\"team\", \"./pages/team.js\"),\n route(\"extensions\", \"./pages/extensions._index.js\"),\n route(\"extensions/:id\", \"./pages/extensions.$id.js\"),\n];\n"]}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { redirect } from "react-router";
3
3
  import { appPath } from "@agent-native/core/client";
4
- import { Spinner } from "../../components/ui/spinner";
4
+ import { Spinner } from "../../components/ui/spinner.js";
5
5
  export function meta() {
6
6
  return [
7
7
  { title: "Agent-Native Dispatch" },
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useActionQuery } from "@agent-native/core/client";
3
- import { AgentsPanel } from "../../components/agents-panel";
4
- import { DispatchShell } from "../../components/dispatch-shell";
3
+ import { AgentsPanel } from "../../components/agents-panel.js";
4
+ import { DispatchShell } from "../../components/dispatch-shell.js";
5
5
  export function meta() {
6
6
  return [{ title: "Agents — Dispatch" }];
7
7
  }
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSearchParams } from "react-router";
3
3
  import { useActionMutation, useActionQuery, isInAgentEmbed, postNavigate, appPath, } from "@agent-native/core/client";
4
4
  import { toast } from "sonner";
5
- import { Button } from "../../components/ui/button";
6
- import { Badge } from "../../components/ui/badge";
5
+ import { Button } from "../../components/ui/button.js";
6
+ import { Badge } from "../../components/ui/badge.js";
7
7
  import { IconCheck, IconX, IconArrowUpRight, IconShieldCheck, IconClock, IconAlertCircle, } from "@tabler/icons-react";
8
8
  export function meta() {
9
9
  return [{ title: "Approval — Dispatch" }];
@@ -3,10 +3,10 @@ import { useMemo, useState } from "react";
3
3
  import { useActionMutation, useActionQuery } from "@agent-native/core/client";
4
4
  import { useOrg } from "@agent-native/core/client/org";
5
5
  import { toast } from "sonner";
6
- import { DispatchShell } from "../../components/dispatch-shell";
7
- import { Button } from "../../components/ui/button";
8
- import { Input } from "../../components/ui/input";
9
- import { Switch } from "../../components/ui/switch";
6
+ import { DispatchShell } from "../../components/dispatch-shell.js";
7
+ import { Button } from "../../components/ui/button.js";
8
+ import { Input } from "../../components/ui/input.js";
9
+ import { Switch } from "../../components/ui/switch.js";
10
10
  export function meta() {
11
11
  return [{ title: "Approvals — Dispatch" }];
12
12
  }
@@ -3,9 +3,9 @@ import { useEffect, useMemo } from "react";
3
3
  import { Link, useParams } from "react-router";
4
4
  import { useActionQuery } from "@agent-native/core/client";
5
5
  import { IconArrowLeft, IconArrowUpRight, IconClockHour4, } from "@tabler/icons-react";
6
- import { DispatchShell } from "../../components/dispatch-shell";
7
- import { Badge } from "../../components/ui/badge";
8
- import { Button } from "../../components/ui/button";
6
+ import { DispatchShell } from "../../components/dispatch-shell.js";
7
+ import { Badge } from "../../components/ui/badge.js";
8
+ import { Button } from "../../components/ui/button.js";
9
9
  function workspaceAppHref(app) {
10
10
  if (app.status === "pending")
11
11
  return app.builderUrl || null;
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useActionQuery } from "@agent-native/core/client";
3
3
  import { IconArrowUpRight, IconApps, IconClockHour4, IconPlus, } from "@tabler/icons-react";
4
- import { AppKeysPopover } from "../../components/app-keys-popover";
5
- import { CreateAppPopover } from "../../components/create-app-popover";
6
- import { DispatchShell } from "../../components/dispatch-shell";
7
- import { Badge } from "../../components/ui/badge";
8
- import { Button } from "../../components/ui/button";
4
+ import { AppKeysPopover } from "../../components/app-keys-popover.js";
5
+ import { CreateAppPopover } from "../../components/create-app-popover.js";
6
+ import { DispatchShell } from "../../components/dispatch-shell.js";
7
+ import { Badge } from "../../components/ui/badge.js";
8
+ import { Button } from "../../components/ui/button.js";
9
9
  function workspaceAppHref(app) {
10
10
  if (app.status === "pending")
11
11
  return app.builderUrl || null;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useActionQuery } from "@agent-native/core/client";
3
- import { DispatchShell } from "../../components/dispatch-shell";
3
+ import { DispatchShell } from "../../components/dispatch-shell.js";
4
4
  export function meta() {
5
5
  return [{ title: "Audit — Dispatch" }];
6
6
  }
@@ -2,12 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { useActionMutation, useActionQuery } from "@agent-native/core/client";
4
4
  import { toast } from "sonner";
5
- import { DispatchShell } from "../../components/dispatch-shell";
6
- import { Button } from "../../components/ui/button";
7
- import { Input } from "../../components/ui/input";
8
- import { Textarea } from "../../components/ui/textarea";
9
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../components/ui/select";
10
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "../../components/ui/alert-dialog";
5
+ import { DispatchShell } from "../../components/dispatch-shell.js";
6
+ import { Button } from "../../components/ui/button.js";
7
+ import { Input } from "../../components/ui/input.js";
8
+ import { Textarea } from "../../components/ui/textarea.js";
9
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../components/ui/select.js";
10
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "../../components/ui/alert-dialog.js";
11
11
  export function meta() {
12
12
  return [{ title: "Destinations — Dispatch" }];
13
13
  }
@@ -0,0 +1,2 @@
1
+ export default function ExtensionViewerRoute(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=extensions.$id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.$id.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/extensions.$id.tsx"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,oBAAoB,4CAE3C"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ToolViewerPage } from "@agent-native/core/client/tools";
3
+ export default function ExtensionViewerRoute() {
4
+ return _jsx(ToolViewerPage, {});
5
+ }
6
+ //# sourceMappingURL=extensions.$id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.$id.js","sourceRoot":"","sources":["../../../src/routes/pages/extensions.$id.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,OAAO,UAAU,oBAAoB;IAC1C,OAAO,KAAC,cAAc,KAAG,CAAC;AAC5B,CAAC","sourcesContent":["import { ToolViewerPage } from \"@agent-native/core/client/tools\";\n\nexport default function ExtensionViewerRoute() {\n return <ToolViewerPage />;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export default function ExtensionsRoute(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=extensions._index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions._index.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/extensions._index.tsx"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,eAAe,4CAEtC"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ToolsListPage } from "@agent-native/core/client/tools";
3
+ export default function ExtensionsRoute() {
4
+ return _jsx(ToolsListPage, {});
5
+ }
6
+ //# sourceMappingURL=extensions._index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions._index.js","sourceRoot":"","sources":["../../../src/routes/pages/extensions._index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,MAAM,CAAC,OAAO,UAAU,eAAe;IACrC,OAAO,KAAC,aAAa,KAAG,CAAC;AAC3B,CAAC","sourcesContent":["import { ToolsListPage } from \"@agent-native/core/client/tools\";\n\nexport default function ExtensionsRoute() {\n return <ToolsListPage />;\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useActionMutation, useActionQuery } from "@agent-native/core/client";
3
3
  import { toast } from "sonner";
4
- import { DispatchShell } from "../../components/dispatch-shell";
5
- import { Button } from "../../components/ui/button";
4
+ import { DispatchShell } from "../../components/dispatch-shell.js";
5
+ import { Button } from "../../components/ui/button.js";
6
6
  export function meta() {
7
7
  return [{ title: "Identities — Dispatch" }];
8
8
  }
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useActionMutation, useActionQuery } from "@agent-native/core/client";
3
3
  import { toast } from "sonner";
4
4
  import { IconCheck, IconCircleDashed, IconKey, IconRefresh, IconShieldCheck, IconWifi, IconWifiOff, } from "@tabler/icons-react";
5
- import { DispatchShell } from "../../components/dispatch-shell";
6
- import { Badge } from "../../components/ui/badge";
7
- import { Button } from "../../components/ui/button";
8
- import { Progress } from "../../components/ui/progress";
5
+ import { DispatchShell } from "../../components/dispatch-shell.js";
6
+ import { Badge } from "../../components/ui/badge.js";
7
+ import { Button } from "../../components/ui/button.js";
8
+ import { Progress } from "../../components/ui/progress.js";
9
9
  export function meta() {
10
10
  return [{ title: "Integrations — Dispatch" }];
11
11
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { MessagingSetupPanel } from "../../components/messaging-setup-panel";
3
- import { DispatchShell } from "../../components/dispatch-shell";
2
+ import { MessagingSetupPanel } from "../../components/messaging-setup-panel.js";
3
+ import { DispatchShell } from "../../components/dispatch-shell.js";
4
4
  export function meta() {
5
5
  return [{ title: "Messaging — Dispatch" }];
6
6
  }
@@ -0,0 +1,5 @@
1
+ export declare function meta(): {
2
+ title: string;
3
+ }[];
4
+ export default function MetricsRoute(): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/metrics.tsx"],"names":[],"mappings":"AAmBA,wBAAgB,IAAI;;IAEnB;AA6gBD,MAAM,CAAC,OAAO,UAAU,YAAY,4CAwHnC"}
@@ -0,0 +1,135 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { useActionQuery } from "@agent-native/core/client";
4
+ import { IconActivity, IconAlertTriangle, IconApps, IconChartBar, IconClockHour4, IconCoin, IconMessages, IconUsersGroup, } from "@tabler/icons-react";
5
+ import { DispatchShell } from "../../components/dispatch-shell.js";
6
+ import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert.js";
7
+ import { Badge } from "../../components/ui/badge.js";
8
+ import { Button } from "../../components/ui/button.js";
9
+ import { Skeleton } from "../../components/ui/skeleton.js";
10
+ import { cn } from "../../lib/utils.js";
11
+ export function meta() {
12
+ return [{ title: "Metrics — Dispatch" }];
13
+ }
14
+ const RANGES = [7, 30, 90];
15
+ function formatCost(cents) {
16
+ if (!Number.isFinite(cents) || cents === 0)
17
+ return "$0.00";
18
+ if (Math.abs(cents) < 1)
19
+ return `${cents.toFixed(3)}¢`;
20
+ if (Math.abs(cents) < 100)
21
+ return `${cents.toFixed(2)}¢`;
22
+ return (cents / 100).toLocaleString(undefined, {
23
+ style: "currency",
24
+ currency: "USD",
25
+ maximumFractionDigits: 2,
26
+ });
27
+ }
28
+ function formatNumber(value) {
29
+ return new Intl.NumberFormat(undefined, {
30
+ notation: value >= 10_000 ? "compact" : "standard",
31
+ maximumFractionDigits: value >= 10_000 ? 1 : 0,
32
+ }).format(value);
33
+ }
34
+ function formatTokens(value) {
35
+ return new Intl.NumberFormat(undefined, {
36
+ notation: "compact",
37
+ maximumFractionDigits: 1,
38
+ }).format(value);
39
+ }
40
+ function timeAgo(timestamp) {
41
+ if (!timestamp)
42
+ return "No activity";
43
+ const diff = Date.now() - timestamp;
44
+ if (diff < 60_000)
45
+ return "just now";
46
+ if (diff < 3_600_000)
47
+ return `${Math.floor(diff / 60_000)}m ago`;
48
+ if (diff < 86_400_000)
49
+ return `${Math.floor(diff / 3_600_000)}h ago`;
50
+ return `${Math.floor(diff / 86_400_000)}d ago`;
51
+ }
52
+ function displayApp(value) {
53
+ const trimmed = value?.trim();
54
+ if (!trimmed || trimmed === "unattributed")
55
+ return "Unattributed";
56
+ return trimmed;
57
+ }
58
+ function maxCost(rows) {
59
+ return rows.reduce((max, row) => Math.max(max, row.costCents), 0);
60
+ }
61
+ function barWidth(value, max) {
62
+ if (max <= 0 || value <= 0)
63
+ return "0%";
64
+ return `${Math.max(4, Math.round((value / max) * 100))}%`;
65
+ }
66
+ function RangeSelector({ value, onChange, }) {
67
+ return (_jsx("div", { className: "flex rounded-md border bg-card p-0.5", children: RANGES.map((range) => (_jsxs(Button, { type: "button", variant: value === range ? "secondary" : "ghost", size: "sm", className: "h-7 px-3 text-xs", onClick: () => onChange(range), children: [range, "d"] }, range))) }));
68
+ }
69
+ function MetricCard({ label, value, detail, icon, }) {
70
+ return (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: label }), _jsx("span", { className: "text-muted-foreground", children: icon })] }), _jsx("div", { className: "text-2xl font-semibold tabular-nums text-foreground", children: value }), _jsx("div", { className: "mt-1 truncate text-xs text-muted-foreground", children: detail })] }));
71
+ }
72
+ function Panel({ title, icon, children, action, }) {
73
+ return (_jsxs("section", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 border-b px-4 py-3", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: icon }), _jsx("h2", { className: "truncate text-sm font-semibold text-foreground", children: title })] }), action] }), _jsx("div", { className: "p-4", children: children })] }));
74
+ }
75
+ function LoadingMetrics() {
76
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5", children: Array.from({ length: 5 }).map((_, index) => (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx(Skeleton, { className: "mb-4 h-4 w-24" }), _jsx(Skeleton, { className: "h-7 w-20" }), _jsx(Skeleton, { className: "mt-3 h-3 w-28" })] }, index))) }), _jsx(Skeleton, { className: "h-80 rounded-lg" })] }));
77
+ }
78
+ function AppSpendRows({ rows }) {
79
+ const max = maxCost(rows);
80
+ if (rows.length === 0) {
81
+ return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No LLM usage recorded for this window." }));
82
+ }
83
+ return (_jsx("div", { className: "space-y-3", children: rows.map((row) => (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium text-foreground", children: displayApp(row.key) }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [formatNumber(row.chatCalls), " chats \u00B7", " ", formatNumber(row.activeUsers), " users"] })] }), _jsxs("div", { className: "shrink-0 text-right", children: [_jsx("div", { className: "font-medium tabular-nums text-foreground", children: formatCost(row.costCents) }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [formatNumber(row.calls), " calls"] })] })] }), _jsx("div", { className: "h-2 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-foreground", style: { width: barWidth(row.costCents, max) } }) })] }, row.key))) }));
84
+ }
85
+ function DailyActivity({ rows }) {
86
+ const max = Math.max(1, rows.reduce((value, row) => Math.max(value, row.calls), 0));
87
+ if (rows.length === 0) {
88
+ return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No activity in this window." }));
89
+ }
90
+ return (_jsx("div", { className: "flex h-44 items-end gap-1", children: rows.map((row) => (_jsxs("div", { className: "group flex min-w-0 flex-1 flex-col items-center gap-2", children: [_jsx("div", { className: "relative flex h-36 w-full items-end rounded-sm bg-muted/60", children: _jsx("div", { className: "w-full rounded-sm bg-foreground transition group-hover:bg-primary", style: { height: `${Math.max(4, (row.calls / max) * 100)}%` } }) }), _jsx("div", { className: "hidden text-[10px] text-muted-foreground sm:block", children: row.date.slice(5) })] }, row.date))) }));
91
+ }
92
+ function AppAccessTable({ rows }) {
93
+ const visibleRows = rows.filter((row) => !row.isDispatch);
94
+ if (visibleRows.length === 0) {
95
+ return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No workspace apps discovered yet." }));
96
+ }
97
+ return (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[720px] text-left text-xs", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "px-2 py-2 font-medium", children: "App" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "Access" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Users" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Chats" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Cost" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Last activity" })] }) }), _jsx("tbody", { children: visibleRows.map((row) => (_jsxs("tr", { className: "border-b last:border-0", children: [_jsxs("td", { className: "px-2 py-3", children: [_jsx("div", { className: "font-medium text-foreground", children: row.name }), _jsx("div", { className: "font-mono text-[11px] text-muted-foreground", children: row.path })] }), _jsx("td", { className: "px-2 py-3", children: _jsx(Badge, { variant: "outline", className: cn(row.status === "pending" &&
98
+ "border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"), children: row.status === "pending" ? "Building" : row.accessLabel }) }), _jsxs("td", { className: "px-2 py-3 text-right tabular-nums", children: [formatNumber(row.usersWithUsage), " /", " ", formatNumber(row.accessUsers)] }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatNumber(row.chatCalls) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatCost(row.costCents) }), _jsx("td", { className: "px-2 py-3 text-right text-muted-foreground", children: timeAgo(row.lastActiveAt) })] }, row.id))) })] }) }));
99
+ }
100
+ function UserTable({ rows }) {
101
+ if (rows.length === 0) {
102
+ return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No users have triggered LLM usage in this window." }));
103
+ }
104
+ return (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[760px] text-left text-xs", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "px-2 py-2 font-medium", children: "User" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "Role" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "Top app" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Chats" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Threads" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Tokens" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Cost" })] }) }), _jsx("tbody", { children: rows.slice(0, 12).map((row) => (_jsxs("tr", { className: "border-b last:border-0", children: [_jsxs("td", { className: "max-w-64 px-2 py-3", children: [_jsx("div", { className: "truncate font-medium text-foreground", children: row.ownerEmail }), _jsx("div", { className: "text-muted-foreground", children: timeAgo(row.lastActiveAt ?? row.lastChatAt) })] }), _jsx("td", { className: "px-2 py-3", children: _jsx(Badge, { variant: "secondary", children: row.role ?? "user" }) }), _jsx("td", { className: "px-2 py-3 text-muted-foreground", children: displayApp(row.topApp) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatNumber(row.chatCalls) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatNumber(row.chatThreads) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatTokens(row.inputTokens + row.outputTokens) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatCost(row.costCents) })] }, row.ownerEmail))) })] }) }));
105
+ }
106
+ function CompactBreakdown({ rows, empty, }) {
107
+ const max = maxCost(rows);
108
+ if (rows.length === 0) {
109
+ return _jsx("div", { className: "text-sm text-muted-foreground", children: empty });
110
+ }
111
+ return (_jsx("div", { className: "space-y-3", children: rows.slice(0, 6).map((row) => (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [_jsx("span", { className: "truncate font-medium text-foreground", children: row.label }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: formatCost(row.costCents) })] }), _jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-muted-foreground", style: { width: barWidth(row.costCents, max) } }) })] }, row.key))) }));
112
+ }
113
+ function RecentTable({ rows }) {
114
+ if (rows.length === 0) {
115
+ return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No recent LLM calls." }));
116
+ }
117
+ return (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[760px] text-left text-xs", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "px-2 py-2 font-medium", children: "When" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "User" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "App" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "Label" }), _jsx("th", { className: "px-2 py-2 font-medium", children: "Model" }), _jsx("th", { className: "px-2 py-2 text-right font-medium", children: "Cost" })] }) }), _jsx("tbody", { children: rows.slice(0, 10).map((row) => (_jsxs("tr", { className: "border-b last:border-0", children: [_jsx("td", { className: "px-2 py-3 text-muted-foreground", children: timeAgo(row.createdAt) }), _jsx("td", { className: "max-w-56 px-2 py-3", children: _jsx("div", { className: "truncate text-foreground", children: row.ownerEmail }) }), _jsx("td", { className: "px-2 py-3 text-muted-foreground", children: displayApp(row.app) }), _jsx("td", { className: "px-2 py-3", children: _jsx(Badge, { variant: "outline", children: row.label }) }), _jsx("td", { className: "max-w-48 px-2 py-3", children: _jsx("div", { className: "truncate text-muted-foreground", children: row.model }) }), _jsx("td", { className: "px-2 py-3 text-right tabular-nums", children: formatCost(row.costCents) })] }, row.id))) })] }) }));
118
+ }
119
+ export default function MetricsRoute() {
120
+ const [sinceDays, setSinceDays] = useState(30);
121
+ const { data, isLoading, error } = useActionQuery("list-dispatch-usage-metrics", { sinceDays }, { refetchInterval: 30_000 });
122
+ const metrics = data;
123
+ const totalTokens = useMemo(() => {
124
+ if (!metrics)
125
+ return 0;
126
+ return (metrics.totals.inputTokens +
127
+ metrics.totals.outputTokens +
128
+ metrics.totals.cacheReadTokens +
129
+ metrics.totals.cacheWriteTokens);
130
+ }, [metrics]);
131
+ return (_jsx(DispatchShell, { title: "Metrics", description: "Workspace-wide LLM spend, chat volume, user activity, and app access.", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("div", { className: "text-sm text-muted-foreground", children: metrics?.access.scope === "organization"
132
+ ? `${metrics.access.totalUsers} workspace users`
133
+ : `${metrics?.access.totalUsers ?? 0} signed-in users` }), _jsx(RangeSelector, { value: sinceDays, onChange: setSinceDays })] }), error ? (_jsxs(Alert, { variant: "destructive", children: [_jsx(IconAlertTriangle, { className: "h-4 w-4" }), _jsx(AlertTitle, { children: "Metrics unavailable" }), _jsx(AlertDescription, { children: error instanceof Error ? error.message : "Unable to load usage." })] })) : null, isLoading && !metrics ? _jsx(LoadingMetrics, {}) : null, metrics ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5", children: [_jsx(MetricCard, { label: "Estimated spend", value: formatCost(metrics.totals.costCents), detail: `${formatTokens(totalTokens)} total tokens`, icon: _jsx(IconCoin, { size: 17 }) }), _jsx(MetricCard, { label: "LLM calls", value: formatNumber(metrics.totals.calls), detail: `${formatNumber(metrics.totals.chatCalls)} chat turns`, icon: _jsx(IconActivity, { size: 17 }) }), _jsx(MetricCard, { label: "Active users", value: formatNumber(metrics.totals.activeUsers), detail: `${formatNumber(metrics.access.totalUsers)} users with access`, icon: _jsx(IconUsersGroup, { size: 17 }) }), _jsx(MetricCard, { label: "Workspace apps", value: formatNumber(metrics.totals.workspaceApps), detail: `${formatNumber(metrics.byApp.length)} with usage`, icon: _jsx(IconApps, { size: 17 }) }), _jsx(MetricCard, { label: "Chat threads", value: formatNumber(metrics.totals.chatThreads), detail: `${formatNumber(metrics.totals.chatMessages)} messages`, icon: _jsx(IconMessages, { size: 17 }) })] }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1.35fr)_minmax(320px,0.65fr)]", children: [_jsx(Panel, { title: "Spend By App", icon: _jsx(IconChartBar, { size: 16 }), children: _jsx(AppSpendRows, { rows: metrics.byApp }) }), _jsx(Panel, { title: "Daily Activity", icon: _jsx(IconClockHour4, { size: 16 }), children: _jsx(DailyActivity, { rows: metrics.daily }) })] }), _jsx(Panel, { title: "Access By App", icon: _jsx(IconApps, { size: 16 }), children: _jsx(AppAccessTable, { rows: metrics.appAccess }) }), _jsx(Panel, { title: "Users", icon: _jsx(IconUsersGroup, { size: 16 }), children: _jsx(UserTable, { rows: metrics.byUser }) }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsx(Panel, { title: "Models", icon: _jsx(IconChartBar, { size: 16 }), children: _jsx(CompactBreakdown, { rows: metrics.byModel, empty: "No model usage in this window." }) }), _jsx(Panel, { title: "Work Types", icon: _jsx(IconActivity, { size: 16 }), children: _jsx(CompactBreakdown, { rows: metrics.byLabel, empty: "No labeled usage in this window." }) })] }), _jsx(Panel, { title: "Recent LLM Calls", icon: _jsx(IconActivity, { size: 16 }), children: _jsx(RecentTable, { rows: metrics.recent }) })] })) : null] }) }));
134
+ }
135
+ //# sourceMappingURL=metrics.js.map