@agent-native/dispatch 0.15.18 → 0.15.19
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.
- package/dist/components/agents-panel.js +1 -1
- package/dist/components/agents-panel.js.map +1 -1
- package/dist/components/dispatch-control-plane.d.ts.map +1 -1
- package/dist/components/dispatch-control-plane.js +1 -1
- package/dist/components/dispatch-control-plane.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +11 -7
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/messaging-setup-panel.js +1 -1
- package/dist/components/messaging-setup-panel.js.map +1 -1
- package/dist/components/task-queue-health.js +1 -1
- package/dist/components/task-queue-health.js.map +1 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/workspace-app-card.js +1 -1
- package/dist/components/workspace-app-card.js.map +1 -1
- package/dist/components/workspace-template-card.js +1 -1
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/routes/pages/$appId.js +1 -1
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/agents.js +1 -1
- package/dist/routes/pages/agents.js.map +1 -1
- package/dist/routes/pages/approval.js +4 -4
- package/dist/routes/pages/approval.js.map +1 -1
- package/dist/routes/pages/approvals.js +2 -2
- package/dist/routes/pages/approvals.js.map +1 -1
- package/dist/routes/pages/apps.$appId.js +1 -1
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.js +3 -3
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/audit.js +1 -1
- package/dist/routes/pages/audit.js.map +1 -1
- package/dist/routes/pages/automations.js +1 -1
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/routes/pages/destinations.js +2 -2
- package/dist/routes/pages/destinations.js.map +1 -1
- package/dist/routes/pages/dreams.js +5 -5
- package/dist/routes/pages/dreams.js.map +1 -1
- package/dist/routes/pages/identities.js +1 -1
- package/dist/routes/pages/identities.js.map +1 -1
- package/dist/routes/pages/integrations.js +2 -2
- package/dist/routes/pages/integrations.js.map +1 -1
- package/dist/routes/pages/metrics.js +4 -4
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/thread-debug.js +5 -5
- package/dist/routes/pages/thread-debug.js.map +1 -1
- package/dist/routes/pages/vault.js +3 -3
- package/dist/routes/pages/vault.js.map +1 -1
- package/dist/routes/pages/workspace.d.ts.map +1 -1
- package/dist/routes/pages/workspace.js +3 -3
- package/dist/routes/pages/workspace.js.map +1 -1
- package/package.json +3 -3
- package/src/components/agents-panel.tsx +1 -1
- package/src/components/dispatch-control-plane.tsx +1 -4
- package/src/components/layout/Layout.spec.tsx +5 -0
- package/src/components/layout/Layout.tsx +62 -32
- package/src/components/messaging-setup-panel.tsx +1 -1
- package/src/components/task-queue-health.tsx +1 -1
- package/src/components/ui/button.tsx +1 -2
- package/src/components/ui/card.tsx +1 -1
- package/src/components/workspace-app-card.tsx +1 -1
- package/src/components/workspace-template-card.tsx +2 -2
- package/src/routes/pages/$appId.tsx +1 -1
- package/src/routes/pages/agents.tsx +1 -1
- package/src/routes/pages/approval.tsx +4 -4
- package/src/routes/pages/approvals.tsx +2 -2
- package/src/routes/pages/apps.$appId.tsx +1 -1
- package/src/routes/pages/apps.tsx +3 -3
- package/src/routes/pages/audit.tsx +1 -1
- package/src/routes/pages/automations.tsx +1 -1
- package/src/routes/pages/destinations.tsx +2 -2
- package/src/routes/pages/dreams.tsx +5 -5
- package/src/routes/pages/identities.tsx +2 -2
- package/src/routes/pages/integrations.tsx +2 -2
- package/src/routes/pages/metrics.tsx +4 -4
- package/src/routes/pages/thread-debug.tsx +6 -6
- package/src/routes/pages/vault.tsx +3 -3
- package/src/routes/pages/workspace.tsx +3 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identities.js","sourceRoot":"","sources":["../../../src/routes/pages/identities.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEpD,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,eAAe;IACrC,MAAM,KAAK,GAAG,cAAc,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACvB,MAAM,WAAW,GAAG,iBAAiB,CAAC,mBAAmB,EAAE;QACzD,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC;KACrD,CAAC,CAAC;IAEH,OAAO,CACL,KAAC,aAAa,IACZ,KAAK,EAAC,YAAY,EAClB,WAAW,EAAC,2CAA2C,YAEtD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CACf,KAAC,gBAAgB,IACf,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,GACnC,CACH,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,2BAA2B,aACxC,mBAAS,SAAS,EAAC,
|
|
1
|
+
{"version":3,"file":"identities.js","sourceRoot":"","sources":["../../../src/routes/pages/identities.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { toast } from \"sonner\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Button } from \"../../components/ui/button\";\n\nexport function meta() {\n return [{ title: \"Identities — Dispatch\" }];\n}\n\nexport default function IdentitiesRoute() {\n const query = useActionQuery(\"list-linked-identities\", {});\n const { data } = query;\n const createToken = useActionMutation(\"create-link-token\", {\n onSuccess: () => toast.success(\"Link token created\"),\n });\n\n return (\n <DispatchShell\n title=\"Identities\"\n description=\"Link external senders to workspace users.\"\n >\n {query.isError ? (\n <ActionQueryError\n error={query.error}\n onRetry={() => void query.refetch()}\n />\n ) : (\n <div className=\"grid gap-4 xl:grid-cols-2\">\n <section className=\"rounded-2xl bg-card p-5\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n Active links\n </h2>\n <div className=\"flex shrink-0 gap-2\">\n <Button\n variant=\"outline\"\n onClick={() => createToken.mutate({ platform: \"slack\" })}\n >\n New Slack token\n </Button>\n <Button\n onClick={() => createToken.mutate({ platform: \"telegram\" })}\n >\n New Telegram token\n </Button>\n </div>\n </div>\n <div className=\"mt-4 space-y-3\">\n {(data?.links || []).map((link: any) => (\n <div\n key={link.id}\n className=\"rounded-xl border bg-muted/30 px-4 py-3\"\n >\n <div className=\"text-sm font-medium text-foreground\">\n {link.externalUserName || link.externalUserId}\n </div>\n <div className=\"mt-1 text-xs text-muted-foreground\">\n {link.platform} → {link.ownerEmail}\n </div>\n </div>\n ))}\n {(data?.links?.length || 0) === 0 && (\n <div className=\"rounded-xl border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No linked identities yet. Generate a token and ask the user to\n send <code>/link TOKEN</code> from Slack or Telegram.\n </div>\n )}\n </div>\n </section>\n\n <section className=\"rounded-2xl bg-card p-5\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n Link tokens\n </h2>\n <div className=\"mt-4 space-y-3\">\n {(data?.tokens || []).map((token: any) => (\n <div key={token.id} className=\"rounded-xl border px-4 py-3\">\n <div className=\"text-sm font-medium text-foreground\">\n /link {token.token}\n </div>\n <div className=\"mt-1 text-xs text-muted-foreground\">\n {token.platform} · expires{\" \"}\n {new Date(token.expiresAt).toLocaleString()}\n {token.claimedAt\n ? ` · claimed by ${token.claimedByExternalUserName || token.claimedByExternalUserId}`\n : \" · waiting to be claimed\"}\n </div>\n </div>\n ))}\n {(data?.tokens?.length || 0) === 0 && (\n <div className=\"rounded-xl border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No active link tokens.\n </div>\n )}\n </div>\n </section>\n </div>\n )}\n </DispatchShell>\n );\n}\n"]}
|
|
@@ -111,7 +111,7 @@ function ConnectorCard({ service, accessMode, }) {
|
|
|
111
111
|
const [open, setOpen] = useState(false);
|
|
112
112
|
const isConnected = service.apps.some((a) => a.configured);
|
|
113
113
|
const appCount = service.apps.length;
|
|
114
|
-
return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: () => setOpen(true), className: "group flex flex-col items-start gap-2 rounded-2xl
|
|
114
|
+
return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: () => setOpen(true), className: "group flex flex-col items-start gap-2 rounded-2xl bg-card p-5 text-left transition-[background-color] hover:bg-card/80 cursor-pointer", children: [_jsxs("div", { className: "flex w-full items-start justify-between gap-2", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-muted", children: _jsx(IconKey, { size: 16, className: "text-muted-foreground" }) }), isConnected ? (_jsxs(Badge, { variant: "secondary", className: "bg-green-500/10 text-green-700 dark:text-green-400 gap-1", children: [_jsx(IconCheck, { size: 12 }), "Connected"] })) : (_jsxs(Badge, { variant: "secondary", className: "bg-amber-500/10 text-amber-700 dark:text-amber-400 gap-1", children: [_jsx(IconCircleDashed, { size: 12 }), "Connect"] }))] }), _jsxs("div", { className: "w-full min-w-0", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: "text-sm font-semibold text-foreground truncate", children: service.label }) }), _jsx(TooltipContent, { children: service.label })] }), _jsx("div", { className: "font-mono text-xs text-muted-foreground/80 truncate", children: service.key })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Used by ", appCount, " ", appCount === 1 ? "app" : "apps"] })] }), _jsx(ConnectDialog, { service: service, open: open, onOpenChange: setOpen, accessMode: accessMode })] }));
|
|
115
115
|
}
|
|
116
116
|
function PerAppDetailRow({ app }) {
|
|
117
117
|
const total = (app.integrations ?? []).length;
|
|
@@ -153,6 +153,6 @@ export default function ConnectionsRoute() {
|
|
|
153
153
|
return (_jsxs(DispatchShell, { title: "Connections", description: "Connect services once. Apps that need them pick up the key automatically.", children: [catalogQuery.isError || accessQuery.isError ? (_jsx(ActionQueryError, { error: catalogQuery.error ?? accessQuery.error, onRetry: () => {
|
|
154
154
|
void catalogQuery.refetch();
|
|
155
155
|
void accessQuery.refetch();
|
|
156
|
-
} })) : null, !catalogQuery.isError && isLoading && services.length === 0 && (_jsx("div", { className: "rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground", children: "Discovering apps and credentials\u2026" })), !catalogQuery.isError && !isLoading && services.length === 0 && (_jsx("div", { className: "rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground", children: "No apps with declared integrations are reachable yet." })), available.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: "mb-3 flex items-baseline justify-between", children: [_jsx("h2", { className: "text-sm font-medium text-foreground", children: "Available to connect" }), _jsx("span", { className: "text-xs text-muted-foreground", children: available.length })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", children: available.map((service) => (_jsx(ConnectorCard, { service: service, accessMode: accessMode }, service.key))) })] })), connected.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: "mb-3 mt-2 flex items-baseline justify-between", children: [_jsx("h2", { className: "text-sm font-medium text-foreground", children: "Connected" }), _jsx("span", { className: "text-xs text-muted-foreground", children: connected.length })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", children: connected.map((service) => (_jsx(ConnectorCard, { service: service, accessMode: accessMode }, service.key))) })] })), apps.length > 0 && (_jsxs(Collapsible, { className: "mt-6 rounded-2xl
|
|
156
|
+
} })) : null, !catalogQuery.isError && isLoading && services.length === 0 && (_jsx("div", { className: "rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground", children: "Discovering apps and credentials\u2026" })), !catalogQuery.isError && !isLoading && services.length === 0 && (_jsx("div", { className: "rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground", children: "No apps with declared integrations are reachable yet." })), available.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: "mb-3 flex items-baseline justify-between", children: [_jsx("h2", { className: "text-sm font-medium text-foreground", children: "Available to connect" }), _jsx("span", { className: "text-xs text-muted-foreground", children: available.length })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", children: available.map((service) => (_jsx(ConnectorCard, { service: service, accessMode: accessMode }, service.key))) })] })), connected.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: "mb-3 mt-2 flex items-baseline justify-between", children: [_jsx("h2", { className: "text-sm font-medium text-foreground", children: "Connected" }), _jsx("span", { className: "text-xs text-muted-foreground", children: connected.length })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3", children: connected.map((service) => (_jsx(ConnectorCard, { service: service, accessMode: accessMode }, service.key))) })] })), apps.length > 0 && (_jsxs(Collapsible, { className: "mt-6 rounded-2xl bg-card", children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center justify-between px-4 py-3 text-sm", children: [_jsxs("span", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(IconPlugConnected, { size: 14 }), "Per-app status"] }), _jsx(IconChevronRight, { size: 14, className: "text-muted-foreground transition group-data-[state=open]:rotate-90" })] }), _jsxs(CollapsibleContent, { children: [_jsx("div", { className: "border-t", children: apps.map((app) => (_jsx(PerAppDetailRow, { app: app }, app.appId))) }), _jsxs("div", { className: "flex items-center justify-end gap-1.5 border-t px-4 py-2.5 text-xs text-muted-foreground", children: [_jsx(IconLink, { size: 12 }), _jsx("a", { href: "/vault", className: "hover:underline", children: "Open vault for advanced sharing" })] })] })] }))] }));
|
|
157
157
|
}
|
|
158
158
|
//# sourceMappingURL=integrations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/routes/pages/integrations.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport {\n IconCheck,\n IconChevronRight,\n IconCircleDashed,\n IconKey,\n IconLink,\n IconPlugConnected,\n} from \"@tabler/icons-react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../../components/ui/collapsible\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"../../components/ui/dialog\";\nimport { Input } from \"../../components/ui/input\";\nimport { Label } from \"../../components/ui/label\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../../components/ui/tooltip\";\n\nexport function meta() {\n return [{ title: \"Connections — Dispatch\" }];\n}\n\ninterface AppRef {\n appId: string;\n appName: string;\n color: string;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n}\n\ninterface Service {\n /** Credential key shared across apps (e.g. `OPENAI_API_KEY`). */\n key: string;\n /** Human label from the first app that declares it (`\"OpenAI\"`, `\"Stripe\"`). */\n label: string;\n /** Apps in the workspace that declare this credential. */\n apps: AppRef[];\n}\n\ninterface CatalogApp {\n appId: string;\n appName: string;\n color: string;\n url: string;\n reachable: boolean;\n integrations?: Array<{\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n }>;\n}\n\nfunction inferProviderFromKey(key: string, label: string): string {\n const haystack = `${key} ${label}`.toLowerCase();\n for (const provider of [\n \"google\",\n \"slack\",\n \"sendgrid\",\n \"github\",\n \"stripe\",\n \"hubspot\",\n \"jira\",\n \"bigquery\",\n \"anthropic\",\n \"openai\",\n ]) {\n if (haystack.includes(provider)) return provider;\n }\n return \"other\";\n}\n\nfunction ConnectDialog({\n service,\n open,\n onOpenChange,\n accessMode,\n}: {\n service: Service;\n open: boolean;\n onOpenChange: (next: boolean) => void;\n accessMode: \"all-apps\" | \"manual\";\n}) {\n const [value, setValue] = useState(\"\");\n const qc = useQueryClient();\n\n const createSecret = useActionMutation(\"create-vault-secret\", {});\n const createGrant = useActionMutation(\"create-vault-grant\", {});\n const syncToApp = useActionMutation(\"sync-vault-to-app\", {});\n\n function reset() {\n setValue(\"\");\n }\n\n async function handleSave() {\n const trimmed = value.trim();\n if (!trimmed) {\n toast.error(\"Enter a value to save\");\n return;\n }\n try {\n // 1. Create the secret (or get the existing one — server treats key as\n // the unique identifier). The server returns { secret: { id, ... } }.\n const created = await createSecret.mutateAsync({\n credentialKey: service.key,\n name: service.label,\n value: trimmed,\n provider: inferProviderFromKey(service.key, service.label),\n });\n const secretId =\n (created as { secret?: { id?: string } })?.secret?.id ??\n (created as { id?: string })?.id;\n if (!secretId) {\n throw new Error(\"Secret created but id missing\");\n }\n\n // 2. Manual mode needs grants; all-apps mode only needs sync.\n if (accessMode === \"manual\") {\n const targets = service.apps.filter((a) => !a.vaultGranted);\n for (const app of targets) {\n try {\n await createGrant.mutateAsync({\n secretId,\n appId: app.appId,\n });\n } catch (err) {\n console.warn(`grant to ${app.appId} failed`, err);\n }\n }\n }\n for (const app of service.apps) {\n try {\n await syncToApp.mutateAsync({ appId: app.appId });\n } catch (err) {\n console.warn(`sync to ${app.appId} failed`, err);\n }\n }\n\n qc.invalidateQueries({\n queryKey: [\"action\", \"list-integrations-catalog\"],\n });\n toast.success(`Connected ${service.label}`);\n onOpenChange(false);\n reset();\n } catch (err: any) {\n toast.error(err?.message ?? \"Failed to save credential\");\n }\n }\n\n const pending =\n createSecret.isPending || createGrant.isPending || syncToApp.isPending;\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) reset();\n onOpenChange(next);\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Connect {service.label}</DialogTitle>\n <DialogDescription>\n Used by{\" \"}\n {service.apps.length === 1\n ? service.apps[0].appName\n : `${service.apps.length} apps`}\n . Saved to the workspace vault and synced to every app that needs\n it.\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <div>\n <Label className=\"text-xs text-muted-foreground\">Key</Label>\n <div className=\"font-mono text-sm\">{service.key}</div>\n </div>\n <div>\n <Label htmlFor=\"connector-value\">Value</Label>\n <Input\n id=\"connector-value\"\n type=\"password\"\n autoComplete=\"off\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder={`Paste your ${service.label} key…`}\n autoFocus\n />\n </div>\n </div>\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={pending}\n >\n Cancel\n </Button>\n <Button onClick={handleSave} disabled={pending || !value.trim()}>\n {pending ? \"Saving…\" : \"Connect\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction ConnectorCard({\n service,\n accessMode,\n}: {\n service: Service;\n accessMode: \"all-apps\" | \"manual\";\n}) {\n const [open, setOpen] = useState(false);\n const isConnected = service.apps.some((a) => a.configured);\n const appCount = service.apps.length;\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n className=\"group flex flex-col items-start gap-2 rounded-2xl border bg-card p-5 text-left transition hover:border-foreground/20 hover:bg-card/80 cursor-pointer\"\n >\n <div className=\"flex w-full items-start justify-between gap-2\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-muted\">\n <IconKey size={16} className=\"text-muted-foreground\" />\n </div>\n {isConnected ? (\n <Badge\n variant=\"secondary\"\n className=\"bg-green-500/10 text-green-700 dark:text-green-400 gap-1\"\n >\n <IconCheck size={12} />\n Connected\n </Badge>\n ) : (\n <Badge\n variant=\"secondary\"\n className=\"bg-amber-500/10 text-amber-700 dark:text-amber-400 gap-1\"\n >\n <IconCircleDashed size={12} />\n Connect\n </Badge>\n )}\n </div>\n <div className=\"w-full min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <div className=\"text-sm font-semibold text-foreground truncate\">\n {service.label}\n </div>\n </TooltipTrigger>\n <TooltipContent>{service.label}</TooltipContent>\n </Tooltip>\n <div className=\"font-mono text-xs text-muted-foreground/80 truncate\">\n {service.key}\n </div>\n </div>\n <div className=\"text-xs text-muted-foreground\">\n Used by {appCount} {appCount === 1 ? \"app\" : \"apps\"}\n </div>\n </button>\n <ConnectDialog\n service={service}\n open={open}\n onOpenChange={setOpen}\n accessMode={accessMode}\n />\n </>\n );\n}\n\nfunction PerAppDetailRow({ app }: { app: CatalogApp }) {\n const total = (app.integrations ?? []).length;\n const ok = (app.integrations ?? []).filter((i) => i.configured).length;\n return (\n <div className=\"flex items-center justify-between border-t px-4 py-2.5 first:border-t-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <div\n className=\"h-5 w-5 rounded text-[10px] font-bold text-white flex items-center justify-center shrink-0\"\n style={{ backgroundColor: app.color }}\n >\n {app.appName.charAt(0).toUpperCase()}\n </div>\n <span className=\"text-sm truncate\">{app.appName}</span>\n {!app.reachable && (\n <span className=\"text-xs text-muted-foreground\">offline</span>\n )}\n </div>\n <span className=\"text-xs text-muted-foreground\">\n {total === 0 ? \"no integrations\" : `${ok}/${total}`}\n </span>\n </div>\n );\n}\n\nexport default function ConnectionsRoute() {\n const catalogQuery = useActionQuery(\"list-integrations-catalog\", {});\n const accessQuery = useActionQuery(\"get-vault-access-settings\", {});\n const { data: catalog, isLoading } = catalogQuery;\n const { data: accessSettings } = accessQuery;\n const apps = (catalog as CatalogApp[]) || [];\n const accessMode =\n (accessSettings as any)?.mode === \"manual\" ? \"manual\" : \"all-apps\";\n\n const services = useMemo<Service[]>(() => {\n const map = new Map<string, Service>();\n for (const app of apps) {\n for (const intg of app.integrations ?? []) {\n if (!map.has(intg.key)) {\n map.set(intg.key, {\n key: intg.key,\n label: intg.label,\n apps: [],\n });\n }\n map.get(intg.key)!.apps.push({\n appId: app.appId,\n appName: app.appName,\n color: app.color,\n configured: intg.configured,\n vaultGranted: intg.vaultGranted,\n vaultSecretId: intg.vaultSecretId,\n });\n }\n }\n return Array.from(map.values()).sort((a, b) =>\n a.label.localeCompare(b.label),\n );\n }, [apps]);\n\n const available = services.filter((s) => !s.apps.some((a) => a.configured));\n const connected = services.filter((s) => s.apps.some((a) => a.configured));\n\n return (\n <DispatchShell\n title=\"Connections\"\n description=\"Connect services once. Apps that need them pick up the key automatically.\"\n >\n {catalogQuery.isError || accessQuery.isError ? (\n <ActionQueryError\n error={catalogQuery.error ?? accessQuery.error}\n onRetry={() => {\n void catalogQuery.refetch();\n void accessQuery.refetch();\n }}\n />\n ) : null}\n\n {!catalogQuery.isError && isLoading && services.length === 0 && (\n <div className=\"rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground\">\n Discovering apps and credentials…\n </div>\n )}\n\n {!catalogQuery.isError && !isLoading && services.length === 0 && (\n <div className=\"rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground\">\n No apps with declared integrations are reachable yet.\n </div>\n )}\n\n {available.length > 0 && (\n <section>\n <div className=\"mb-3 flex items-baseline justify-between\">\n <h2 className=\"text-sm font-medium text-foreground\">\n Available to connect\n </h2>\n <span className=\"text-xs text-muted-foreground\">\n {available.length}\n </span>\n </div>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {available.map((service) => (\n <ConnectorCard\n key={service.key}\n service={service}\n accessMode={accessMode}\n />\n ))}\n </div>\n </section>\n )}\n\n {connected.length > 0 && (\n <section>\n <div className=\"mb-3 mt-2 flex items-baseline justify-between\">\n <h2 className=\"text-sm font-medium text-foreground\">Connected</h2>\n <span className=\"text-xs text-muted-foreground\">\n {connected.length}\n </span>\n </div>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {connected.map((service) => (\n <ConnectorCard\n key={service.key}\n service={service}\n accessMode={accessMode}\n />\n ))}\n </div>\n </section>\n )}\n\n {apps.length > 0 && (\n <Collapsible className=\"mt-6 rounded-2xl border bg-card\">\n <CollapsibleTrigger className=\"group flex w-full items-center justify-between px-4 py-3 text-sm\">\n <span className=\"flex items-center gap-2 text-muted-foreground\">\n <IconPlugConnected size={14} />\n Per-app status\n </span>\n <IconChevronRight\n size={14}\n className=\"text-muted-foreground transition group-data-[state=open]:rotate-90\"\n />\n </CollapsibleTrigger>\n <CollapsibleContent>\n <div className=\"border-t\">\n {apps.map((app) => (\n <PerAppDetailRow key={app.appId} app={app} />\n ))}\n </div>\n <div className=\"flex items-center justify-end gap-1.5 border-t px-4 py-2.5 text-xs text-muted-foreground\">\n <IconLink size={12} />\n <a href=\"/vault\" className=\"hover:underline\">\n Open vault for advanced sharing\n </a>\n </div>\n </CollapsibleContent>\n </Collapsible>\n )}\n </DispatchShell>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/routes/pages/integrations.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport {\n IconCheck,\n IconChevronRight,\n IconCircleDashed,\n IconKey,\n IconLink,\n IconPlugConnected,\n} from \"@tabler/icons-react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../../components/ui/collapsible\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"../../components/ui/dialog\";\nimport { Input } from \"../../components/ui/input\";\nimport { Label } from \"../../components/ui/label\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../../components/ui/tooltip\";\n\nexport function meta() {\n return [{ title: \"Connections — Dispatch\" }];\n}\n\ninterface AppRef {\n appId: string;\n appName: string;\n color: string;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n}\n\ninterface Service {\n /** Credential key shared across apps (e.g. `OPENAI_API_KEY`). */\n key: string;\n /** Human label from the first app that declares it (`\"OpenAI\"`, `\"Stripe\"`). */\n label: string;\n /** Apps in the workspace that declare this credential. */\n apps: AppRef[];\n}\n\ninterface CatalogApp {\n appId: string;\n appName: string;\n color: string;\n url: string;\n reachable: boolean;\n integrations?: Array<{\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n }>;\n}\n\nfunction inferProviderFromKey(key: string, label: string): string {\n const haystack = `${key} ${label}`.toLowerCase();\n for (const provider of [\n \"google\",\n \"slack\",\n \"sendgrid\",\n \"github\",\n \"stripe\",\n \"hubspot\",\n \"jira\",\n \"bigquery\",\n \"anthropic\",\n \"openai\",\n ]) {\n if (haystack.includes(provider)) return provider;\n }\n return \"other\";\n}\n\nfunction ConnectDialog({\n service,\n open,\n onOpenChange,\n accessMode,\n}: {\n service: Service;\n open: boolean;\n onOpenChange: (next: boolean) => void;\n accessMode: \"all-apps\" | \"manual\";\n}) {\n const [value, setValue] = useState(\"\");\n const qc = useQueryClient();\n\n const createSecret = useActionMutation(\"create-vault-secret\", {});\n const createGrant = useActionMutation(\"create-vault-grant\", {});\n const syncToApp = useActionMutation(\"sync-vault-to-app\", {});\n\n function reset() {\n setValue(\"\");\n }\n\n async function handleSave() {\n const trimmed = value.trim();\n if (!trimmed) {\n toast.error(\"Enter a value to save\");\n return;\n }\n try {\n // 1. Create the secret (or get the existing one — server treats key as\n // the unique identifier). The server returns { secret: { id, ... } }.\n const created = await createSecret.mutateAsync({\n credentialKey: service.key,\n name: service.label,\n value: trimmed,\n provider: inferProviderFromKey(service.key, service.label),\n });\n const secretId =\n (created as { secret?: { id?: string } })?.secret?.id ??\n (created as { id?: string })?.id;\n if (!secretId) {\n throw new Error(\"Secret created but id missing\");\n }\n\n // 2. Manual mode needs grants; all-apps mode only needs sync.\n if (accessMode === \"manual\") {\n const targets = service.apps.filter((a) => !a.vaultGranted);\n for (const app of targets) {\n try {\n await createGrant.mutateAsync({\n secretId,\n appId: app.appId,\n });\n } catch (err) {\n console.warn(`grant to ${app.appId} failed`, err);\n }\n }\n }\n for (const app of service.apps) {\n try {\n await syncToApp.mutateAsync({ appId: app.appId });\n } catch (err) {\n console.warn(`sync to ${app.appId} failed`, err);\n }\n }\n\n qc.invalidateQueries({\n queryKey: [\"action\", \"list-integrations-catalog\"],\n });\n toast.success(`Connected ${service.label}`);\n onOpenChange(false);\n reset();\n } catch (err: any) {\n toast.error(err?.message ?? \"Failed to save credential\");\n }\n }\n\n const pending =\n createSecret.isPending || createGrant.isPending || syncToApp.isPending;\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) reset();\n onOpenChange(next);\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Connect {service.label}</DialogTitle>\n <DialogDescription>\n Used by{\" \"}\n {service.apps.length === 1\n ? service.apps[0].appName\n : `${service.apps.length} apps`}\n . Saved to the workspace vault and synced to every app that needs\n it.\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-3\">\n <div>\n <Label className=\"text-xs text-muted-foreground\">Key</Label>\n <div className=\"font-mono text-sm\">{service.key}</div>\n </div>\n <div>\n <Label htmlFor=\"connector-value\">Value</Label>\n <Input\n id=\"connector-value\"\n type=\"password\"\n autoComplete=\"off\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder={`Paste your ${service.label} key…`}\n autoFocus\n />\n </div>\n </div>\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={pending}\n >\n Cancel\n </Button>\n <Button onClick={handleSave} disabled={pending || !value.trim()}>\n {pending ? \"Saving…\" : \"Connect\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction ConnectorCard({\n service,\n accessMode,\n}: {\n service: Service;\n accessMode: \"all-apps\" | \"manual\";\n}) {\n const [open, setOpen] = useState(false);\n const isConnected = service.apps.some((a) => a.configured);\n const appCount = service.apps.length;\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n className=\"group flex flex-col items-start gap-2 rounded-2xl bg-card p-5 text-left transition-[background-color] hover:bg-card/80 cursor-pointer\"\n >\n <div className=\"flex w-full items-start justify-between gap-2\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-muted\">\n <IconKey size={16} className=\"text-muted-foreground\" />\n </div>\n {isConnected ? (\n <Badge\n variant=\"secondary\"\n className=\"bg-green-500/10 text-green-700 dark:text-green-400 gap-1\"\n >\n <IconCheck size={12} />\n Connected\n </Badge>\n ) : (\n <Badge\n variant=\"secondary\"\n className=\"bg-amber-500/10 text-amber-700 dark:text-amber-400 gap-1\"\n >\n <IconCircleDashed size={12} />\n Connect\n </Badge>\n )}\n </div>\n <div className=\"w-full min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <div className=\"text-sm font-semibold text-foreground truncate\">\n {service.label}\n </div>\n </TooltipTrigger>\n <TooltipContent>{service.label}</TooltipContent>\n </Tooltip>\n <div className=\"font-mono text-xs text-muted-foreground/80 truncate\">\n {service.key}\n </div>\n </div>\n <div className=\"text-xs text-muted-foreground\">\n Used by {appCount} {appCount === 1 ? \"app\" : \"apps\"}\n </div>\n </button>\n <ConnectDialog\n service={service}\n open={open}\n onOpenChange={setOpen}\n accessMode={accessMode}\n />\n </>\n );\n}\n\nfunction PerAppDetailRow({ app }: { app: CatalogApp }) {\n const total = (app.integrations ?? []).length;\n const ok = (app.integrations ?? []).filter((i) => i.configured).length;\n return (\n <div className=\"flex items-center justify-between border-t px-4 py-2.5 first:border-t-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <div\n className=\"h-5 w-5 rounded text-[10px] font-bold text-white flex items-center justify-center shrink-0\"\n style={{ backgroundColor: app.color }}\n >\n {app.appName.charAt(0).toUpperCase()}\n </div>\n <span className=\"text-sm truncate\">{app.appName}</span>\n {!app.reachable && (\n <span className=\"text-xs text-muted-foreground\">offline</span>\n )}\n </div>\n <span className=\"text-xs text-muted-foreground\">\n {total === 0 ? \"no integrations\" : `${ok}/${total}`}\n </span>\n </div>\n );\n}\n\nexport default function ConnectionsRoute() {\n const catalogQuery = useActionQuery(\"list-integrations-catalog\", {});\n const accessQuery = useActionQuery(\"get-vault-access-settings\", {});\n const { data: catalog, isLoading } = catalogQuery;\n const { data: accessSettings } = accessQuery;\n const apps = (catalog as CatalogApp[]) || [];\n const accessMode =\n (accessSettings as any)?.mode === \"manual\" ? \"manual\" : \"all-apps\";\n\n const services = useMemo<Service[]>(() => {\n const map = new Map<string, Service>();\n for (const app of apps) {\n for (const intg of app.integrations ?? []) {\n if (!map.has(intg.key)) {\n map.set(intg.key, {\n key: intg.key,\n label: intg.label,\n apps: [],\n });\n }\n map.get(intg.key)!.apps.push({\n appId: app.appId,\n appName: app.appName,\n color: app.color,\n configured: intg.configured,\n vaultGranted: intg.vaultGranted,\n vaultSecretId: intg.vaultSecretId,\n });\n }\n }\n return Array.from(map.values()).sort((a, b) =>\n a.label.localeCompare(b.label),\n );\n }, [apps]);\n\n const available = services.filter((s) => !s.apps.some((a) => a.configured));\n const connected = services.filter((s) => s.apps.some((a) => a.configured));\n\n return (\n <DispatchShell\n title=\"Connections\"\n description=\"Connect services once. Apps that need them pick up the key automatically.\"\n >\n {catalogQuery.isError || accessQuery.isError ? (\n <ActionQueryError\n error={catalogQuery.error ?? accessQuery.error}\n onRetry={() => {\n void catalogQuery.refetch();\n void accessQuery.refetch();\n }}\n />\n ) : null}\n\n {!catalogQuery.isError && isLoading && services.length === 0 && (\n <div className=\"rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground\">\n Discovering apps and credentials…\n </div>\n )}\n\n {!catalogQuery.isError && !isLoading && services.length === 0 && (\n <div className=\"rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground\">\n No apps with declared integrations are reachable yet.\n </div>\n )}\n\n {available.length > 0 && (\n <section>\n <div className=\"mb-3 flex items-baseline justify-between\">\n <h2 className=\"text-sm font-medium text-foreground\">\n Available to connect\n </h2>\n <span className=\"text-xs text-muted-foreground\">\n {available.length}\n </span>\n </div>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {available.map((service) => (\n <ConnectorCard\n key={service.key}\n service={service}\n accessMode={accessMode}\n />\n ))}\n </div>\n </section>\n )}\n\n {connected.length > 0 && (\n <section>\n <div className=\"mb-3 mt-2 flex items-baseline justify-between\">\n <h2 className=\"text-sm font-medium text-foreground\">Connected</h2>\n <span className=\"text-xs text-muted-foreground\">\n {connected.length}\n </span>\n </div>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {connected.map((service) => (\n <ConnectorCard\n key={service.key}\n service={service}\n accessMode={accessMode}\n />\n ))}\n </div>\n </section>\n )}\n\n {apps.length > 0 && (\n <Collapsible className=\"mt-6 rounded-2xl bg-card\">\n <CollapsibleTrigger className=\"group flex w-full items-center justify-between px-4 py-3 text-sm\">\n <span className=\"flex items-center gap-2 text-muted-foreground\">\n <IconPlugConnected size={14} />\n Per-app status\n </span>\n <IconChevronRight\n size={14}\n className=\"text-muted-foreground transition group-data-[state=open]:rotate-90\"\n />\n </CollapsibleTrigger>\n <CollapsibleContent>\n <div className=\"border-t\">\n {apps.map((app) => (\n <PerAppDetailRow key={app.appId} app={app} />\n ))}\n </div>\n <div className=\"flex items-center justify-end gap-1.5 border-t px-4 py-2.5 text-xs text-muted-foreground\">\n <IconLink size={12} />\n <a href=\"/vault\" className=\"hover:underline\">\n Open vault for advanced sharing\n </a>\n </div>\n </CollapsibleContent>\n </Collapsible>\n )}\n </DispatchShell>\n );\n}\n"]}
|
|
@@ -91,16 +91,16 @@ function barWidth(value, max) {
|
|
|
91
91
|
return `${Math.max(4, Math.round((value / max) * 100))}%`;
|
|
92
92
|
}
|
|
93
93
|
function RangeSelector({ value, onChange, }) {
|
|
94
|
-
return (_jsx("div", { className: "flex rounded-md
|
|
94
|
+
return (_jsx("div", { className: "flex rounded-md 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))) }));
|
|
95
95
|
}
|
|
96
96
|
function MetricCard({ label, value, detail, icon, }) {
|
|
97
|
-
return (_jsxs("div", { className: "rounded-lg
|
|
97
|
+
return (_jsxs("div", { className: "rounded-lg 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 })] }));
|
|
98
98
|
}
|
|
99
99
|
function Panel({ title, icon, children, action, }) {
|
|
100
|
-
return (_jsxs("section", { className: "rounded-lg
|
|
100
|
+
return (_jsxs("section", { className: "rounded-lg 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 })] }));
|
|
101
101
|
}
|
|
102
102
|
function LoadingMetrics() {
|
|
103
|
-
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
|
|
103
|
+
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 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" })] }));
|
|
104
104
|
}
|
|
105
105
|
function AppSpendRows({ rows, billing, }) {
|
|
106
106
|
const max = maxSpend(rows, billing);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/routes/pages/metrics.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n IconActivity,\n IconAlertTriangle,\n IconApps,\n IconChartBar,\n IconClockHour4,\n IconCoin,\n IconMessages,\n IconUsersGroup,\n} from \"@tabler/icons-react\";\nimport { useMemo, useState, type ReactNode } from \"react\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Alert, AlertDescription, AlertTitle } from \"../../components/ui/alert\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { cn } from \"../../lib/utils\";\n\nexport function meta() {\n return [{ title: \"Metrics — Dispatch\" }];\n}\n\ninterface UsageMetricBucket {\n key: string;\n label: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n activeUsers: number;\n lastActiveAt: number | null;\n}\n\ninterface 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\ninterface AppAccessMetric {\n id: string;\n name: string;\n path: string;\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n isDispatch: boolean;\n accessLabel: string;\n accessUsers: number;\n usersWithUsage: number;\n usageCalls: number;\n chatCalls: number;\n costCents: number;\n lastActiveAt: number | null;\n}\n\ninterface DailyUsageMetric {\n date: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n activeUsers: number;\n}\n\ninterface 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 costCents: number;\n}\n\ninterface UsageBillingMode {\n unit: \"usd\" | \"builder-credits\";\n label: string;\n shortLabel: string;\n source: \"estimated-provider-cost\" | \"builder-agent-credits\";\n hardCostMarginMultiplier?: number;\n creditsPerUsd?: number;\n}\n\ninterface DispatchUsageMetrics {\n billing?: UsageBillingMode;\n sinceDays: 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\nconst RANGES = [7, 30, 90] as const;\n\nconst USD_BILLING: UsageBillingMode = {\n unit: \"usd\",\n label: \"Estimated spend\",\n shortLabel: \"Cost\",\n source: \"estimated-provider-cost\",\n};\n\nfunction displayAmountFromCostCents(\n cents: number,\n billing: UsageBillingMode,\n): number {\n if (billing.unit !== \"builder-credits\") return cents;\n const margin = billing.hardCostMarginMultiplier ?? 1.25;\n const creditsPerUsd = billing.creditsPerUsd ?? 20;\n const credits = (cents / 100) * margin * creditsPerUsd;\n return credits <= 0 ? 0 : Math.ceil(credits * 1000) / 1000;\n}\n\nfunction formatCredits(credits: number): string {\n if (!Number.isFinite(credits) || credits === 0) return \"0 credits\";\n const maximumFractionDigits = credits < 1 ? 3 : credits < 10 ? 2 : 1;\n const value = credits.toLocaleString(undefined, {\n maximumFractionDigits,\n });\n return `${value} ${credits === 1 ? \"credit\" : \"credits\"}`;\n}\n\nfunction formatSpend(cents: number, billing: UsageBillingMode): string {\n if (billing.unit === \"builder-credits\") {\n return formatCredits(displayAmountFromCostCents(cents, billing));\n }\n if (!Number.isFinite(cents) || cents === 0) return \"$0.00\";\n if (Math.abs(cents) < 1) return `${cents.toFixed(3)}¢`;\n if (Math.abs(cents) < 100) return `${cents.toFixed(2)}¢`;\n return (cents / 100).toLocaleString(undefined, {\n style: \"currency\",\n currency: \"USD\",\n maximumFractionDigits: 2,\n });\n}\n\nfunction formatNumber(value: number): string {\n return new Intl.NumberFormat(undefined, {\n notation: value >= 10_000 ? \"compact\" : \"standard\",\n maximumFractionDigits: value >= 10_000 ? 1 : 0,\n }).format(value);\n}\n\nfunction formatTokens(value: number): string {\n return new Intl.NumberFormat(undefined, {\n notation: \"compact\",\n maximumFractionDigits: 1,\n }).format(value);\n}\n\nfunction timeAgo(timestamp: number | null): string {\n if (!timestamp) return \"No activity\";\n const diff = Date.now() - timestamp;\n if (diff < 60_000) return \"just now\";\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;\n return `${Math.floor(diff / 86_400_000)}d ago`;\n}\n\nfunction displayApp(value: string | null | undefined): string {\n const trimmed = value?.trim();\n if (!trimmed || trimmed === \"unattributed\") return \"Unattributed\";\n return trimmed;\n}\n\nfunction maxSpend(\n rows: Array<{ costCents: number }>,\n billing: UsageBillingMode,\n): number {\n return rows.reduce(\n (max, row) =>\n Math.max(max, displayAmountFromCostCents(row.costCents, billing)),\n 0,\n );\n}\n\nfunction barWidth(value: number, max: number): string {\n if (max <= 0 || value <= 0) return \"0%\";\n return `${Math.max(4, Math.round((value / max) * 100))}%`;\n}\n\nfunction RangeSelector({\n value,\n onChange,\n}: {\n value: number;\n onChange: (value: number) => void;\n}) {\n return (\n <div className=\"flex rounded-md border bg-card p-0.5\">\n {RANGES.map((range) => (\n <Button\n key={range}\n type=\"button\"\n variant={value === range ? \"secondary\" : \"ghost\"}\n size=\"sm\"\n className=\"h-7 px-3 text-xs\"\n onClick={() => onChange(range)}\n >\n {range}d\n </Button>\n ))}\n </div>\n );\n}\n\nfunction MetricCard({\n label,\n value,\n detail,\n icon,\n}: {\n label: string;\n value: string;\n detail: string;\n icon: ReactNode;\n}) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </span>\n <span className=\"text-muted-foreground\">{icon}</span>\n </div>\n <div className=\"text-2xl font-semibold tabular-nums text-foreground\">\n {value}\n </div>\n <div className=\"mt-1 truncate text-xs text-muted-foreground\">\n {detail}\n </div>\n </div>\n );\n}\n\nfunction Panel({\n title,\n icon,\n children,\n action,\n}: {\n title: string;\n icon: ReactNode;\n children: ReactNode;\n action?: ReactNode;\n}) {\n return (\n <section className=\"rounded-lg border bg-card\">\n <div className=\"flex items-center justify-between gap-3 border-b px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"text-muted-foreground\">{icon}</span>\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {title}\n </h2>\n </div>\n {action}\n </div>\n <div className=\"p-4\">{children}</div>\n </section>\n );\n}\n\nfunction LoadingMetrics() {\n return (\n <div className=\"space-y-4\">\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-5\">\n {Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <Skeleton className=\"mb-4 h-4 w-24\" />\n <Skeleton className=\"h-7 w-20\" />\n <Skeleton className=\"mt-3 h-3 w-28\" />\n </div>\n ))}\n </div>\n <Skeleton className=\"h-80 rounded-lg\" />\n </div>\n );\n}\n\nfunction AppSpendRows({\n rows,\n billing,\n}: {\n rows: UsageMetricBucket[];\n billing: UsageBillingMode;\n}) {\n const max = maxSpend(rows, billing);\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No LLM usage recorded for this window.\n </div>\n );\n }\n return (\n <div className=\"space-y-3\">\n {rows.map((row) => (\n <div key={row.key} className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-sm\">\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium text-foreground\">\n {displayApp(row.key)}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatNumber(row.chatCalls)} chats ·{\" \"}\n {formatNumber(row.activeUsers)} users\n </div>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"font-medium tabular-nums text-foreground\">\n {formatSpend(row.costCents, billing)}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatNumber(row.calls)} calls\n </div>\n </div>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-foreground\"\n style={{\n width: barWidth(\n displayAmountFromCostCents(row.costCents, billing),\n max,\n ),\n }}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction DailyActivity({ rows }: { rows: DailyUsageMetric[] }) {\n const max = Math.max(\n 1,\n rows.reduce((value, row) => Math.max(value, row.calls), 0),\n );\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No activity in this window.\n </div>\n );\n }\n return (\n <div className=\"flex h-44 items-end gap-1\">\n {rows.map((row) => (\n <div\n key={row.date}\n className=\"group flex min-w-0 flex-1 flex-col items-center gap-2\"\n >\n <div className=\"relative flex h-36 w-full items-end rounded-sm bg-muted/60\">\n <div\n className=\"w-full rounded-sm bg-foreground transition group-hover:bg-primary\"\n style={{ height: `${Math.max(4, (row.calls / max) * 100)}%` }}\n />\n </div>\n <div className=\"hidden text-[10px] text-muted-foreground sm:block\">\n {row.date.slice(5)}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction AppAccessTable({\n rows,\n billing,\n}: {\n rows: AppAccessMetric[];\n billing: UsageBillingMode;\n}) {\n const visibleRows = rows.filter((row) => !row.isDispatch);\n if (visibleRows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No workspace apps discovered yet.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[720px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">App</th>\n <th className=\"px-2 py-2 font-medium\">Access</th>\n <th className=\"px-2 py-2 text-right font-medium\">Users</th>\n <th className=\"px-2 py-2 text-right font-medium\">Chats</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n <th className=\"px-2 py-2 text-right font-medium\">Last activity</th>\n </tr>\n </thead>\n <tbody>\n {visibleRows.map((row) => (\n <tr key={row.id} className=\"border-b last:border-0\">\n <td className=\"px-2 py-3\">\n <div className=\"font-medium text-foreground\">{row.name}</div>\n <div className=\"font-mono text-[11px] text-muted-foreground\">\n {row.path}\n </div>\n </td>\n <td className=\"px-2 py-3\">\n <Badge\n variant=\"outline\"\n className={cn(\n row.status === \"pending\" &&\n \"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\",\n )}\n >\n {row.status === \"pending\"\n ? row.statusLabel || \"Builder branch\"\n : row.accessLabel}\n </Badge>\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.usersWithUsage)} /{\" \"}\n {formatNumber(row.accessUsers)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatCalls)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n <td className=\"px-2 py-3 text-right text-muted-foreground\">\n {timeAgo(row.lastActiveAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction UserTable({\n rows,\n billing,\n}: {\n rows: UserUsageMetric[];\n billing: UsageBillingMode;\n}) {\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No users have triggered LLM usage in this window.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[760px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">User</th>\n <th className=\"px-2 py-2 font-medium\">Role</th>\n <th className=\"px-2 py-2 font-medium\">Top app</th>\n <th className=\"px-2 py-2 text-right font-medium\">Chats</th>\n <th className=\"px-2 py-2 text-right font-medium\">Threads</th>\n <th className=\"px-2 py-2 text-right font-medium\">Tokens</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.slice(0, 12).map((row) => (\n <tr key={row.ownerEmail} className=\"border-b last:border-0\">\n <td className=\"max-w-64 px-2 py-3\">\n <div className=\"truncate font-medium text-foreground\">\n {row.ownerEmail}\n </div>\n <div className=\"text-muted-foreground\">\n {timeAgo(row.lastActiveAt ?? row.lastChatAt)}\n </div>\n </td>\n <td className=\"px-2 py-3\">\n <Badge variant=\"secondary\">{row.role ?? \"user\"}</Badge>\n </td>\n <td className=\"px-2 py-3 text-muted-foreground\">\n {displayApp(row.topApp)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatCalls)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatThreads)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatTokens(row.inputTokens + row.outputTokens)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction CompactBreakdown({\n rows,\n empty,\n billing,\n}: {\n rows: UsageMetricBucket[];\n empty: string;\n billing: UsageBillingMode;\n}) {\n const max = maxSpend(rows, billing);\n if (rows.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{empty}</div>;\n }\n return (\n <div className=\"space-y-3\">\n {rows.slice(0, 6).map((row) => (\n <div key={row.key} className=\"space-y-1\">\n <div className=\"flex items-center justify-between gap-3 text-xs\">\n <span className=\"truncate font-medium text-foreground\">\n {row.label}\n </span>\n <span className=\"shrink-0 tabular-nums text-muted-foreground\">\n {formatSpend(row.costCents, billing)}\n </span>\n </div>\n <div className=\"h-1.5 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-muted-foreground\"\n style={{\n width: barWidth(\n displayAmountFromCostCents(row.costCents, billing),\n max,\n ),\n }}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction RecentTable({\n rows,\n billing,\n}: {\n rows: RecentUsageMetric[];\n billing: UsageBillingMode;\n}) {\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No recent LLM calls.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[760px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">When</th>\n <th className=\"px-2 py-2 font-medium\">User</th>\n <th className=\"px-2 py-2 font-medium\">App</th>\n <th className=\"px-2 py-2 font-medium\">Label</th>\n <th className=\"px-2 py-2 font-medium\">Model</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.slice(0, 10).map((row) => (\n <tr key={row.id} className=\"border-b last:border-0\">\n <td className=\"px-2 py-3 text-muted-foreground\">\n {timeAgo(row.createdAt)}\n </td>\n <td className=\"max-w-56 px-2 py-3\">\n <div className=\"truncate text-foreground\">{row.ownerEmail}</div>\n </td>\n <td className=\"px-2 py-3 text-muted-foreground\">\n {displayApp(row.app)}\n </td>\n <td className=\"px-2 py-3\">\n <Badge variant=\"outline\">{row.label}</Badge>\n </td>\n <td className=\"max-w-48 px-2 py-3\">\n <div className=\"truncate text-muted-foreground\">\n {row.model}\n </div>\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nexport default function MetricsRoute() {\n const t = useT();\n const [sinceDays, setSinceDays] = useState(30);\n const { data, isLoading, error } = useActionQuery(\n \"list-dispatch-usage-metrics\",\n { sinceDays },\n );\n const metrics = data as DispatchUsageMetrics | undefined;\n const billing = metrics?.billing ?? USD_BILLING;\n const totalTokens = useMemo(() => {\n if (!metrics) return 0;\n return (\n metrics.totals.inputTokens +\n metrics.totals.outputTokens +\n metrics.totals.cacheReadTokens +\n metrics.totals.cacheWriteTokens\n );\n }, [metrics]);\n\n return (\n <DispatchShell\n title={t(\"dispatch.nav.metrics\")}\n description={\n billing.unit === \"builder-credits\"\n ? t(\"dispatch.pages.metricsDescriptionBuilder\")\n : t(\"dispatch.pages.metricsDescriptionLlm\")\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"text-sm text-muted-foreground\">\n {metrics?.access.scope === \"organization\"\n ? `${metrics.access.totalUsers} workspace users`\n : `${metrics?.access.totalUsers ?? 0} signed-in users`}\n </div>\n <RangeSelector value={sinceDays} onChange={setSinceDays} />\n </div>\n\n {error ? (\n <Alert variant=\"destructive\">\n <IconAlertTriangle className=\"h-4 w-4\" />\n <AlertTitle>{t(\"dispatch.pages.metricsUnavailable\")}</AlertTitle>\n <AlertDescription>\n {error instanceof Error\n ? error.message\n : t(\"dispatch.pages.unableToLoadUsage\")}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {isLoading && !metrics ? <LoadingMetrics /> : null}\n\n {metrics ? (\n <>\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-5\">\n <MetricCard\n label={billing.label}\n value={formatSpend(metrics.totals.costCents, billing)}\n detail={`${formatTokens(totalTokens)} total tokens`}\n icon={<IconCoin size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.llmCalls\")}\n value={formatNumber(metrics.totals.calls)}\n detail={`${formatNumber(metrics.totals.chatCalls)} chat turns`}\n icon={<IconActivity size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.activeUsers\")}\n value={formatNumber(metrics.totals.activeUsers)}\n detail={`${formatNumber(metrics.access.totalUsers)} users with access`}\n icon={<IconUsersGroup size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.workspaceAppsStat\")}\n value={formatNumber(metrics.totals.workspaceApps)}\n detail={`${formatNumber(metrics.byApp.length)} with usage`}\n icon={<IconApps size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.chatThreads\")}\n value={formatNumber(metrics.totals.chatThreads)}\n detail={`${formatNumber(metrics.totals.chatMessages)} messages`}\n icon={<IconMessages size={17} />}\n />\n </div>\n\n <div className=\"grid gap-4 xl:grid-cols-[minmax(0,1.35fr)_minmax(320px,0.65fr)]\">\n <Panel\n title={\n billing.unit === \"builder-credits\"\n ? \"Credit Spend By App\"\n : \"Spend By App\"\n }\n icon={<IconChartBar size={16} />}\n >\n <AppSpendRows rows={metrics.byApp} billing={billing} />\n </Panel>\n <Panel title=\"Daily Activity\" icon={<IconClockHour4 size={16} />}>\n <DailyActivity rows={metrics.daily} />\n </Panel>\n </div>\n\n <Panel title=\"Access By App\" icon={<IconApps size={16} />}>\n <AppAccessTable rows={metrics.appAccess} billing={billing} />\n </Panel>\n\n <Panel title=\"Users\" icon={<IconUsersGroup size={16} />}>\n <UserTable rows={metrics.byUser} billing={billing} />\n </Panel>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Panel title=\"Models\" icon={<IconChartBar size={16} />}>\n <CompactBreakdown\n rows={metrics.byModel}\n empty=\"No model usage in this window.\"\n billing={billing}\n />\n </Panel>\n <Panel title=\"Work Types\" icon={<IconActivity size={16} />}>\n <CompactBreakdown\n rows={metrics.byLabel}\n empty=\"No labeled usage in this window.\"\n billing={billing}\n />\n </Panel>\n </div>\n\n <Panel title=\"Recent LLM Calls\" icon={<IconActivity size={16} />}>\n <RecentTable rows={metrics.recent} billing={billing} />\n </Panel>\n </>\n ) : null}\n </div>\n </DispatchShell>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/routes/pages/metrics.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n IconActivity,\n IconAlertTriangle,\n IconApps,\n IconChartBar,\n IconClockHour4,\n IconCoin,\n IconMessages,\n IconUsersGroup,\n} from \"@tabler/icons-react\";\nimport { useMemo, useState, type ReactNode } from \"react\";\n\nimport { DispatchShell } from \"../../components/dispatch-shell\";\nimport { Alert, AlertDescription, AlertTitle } from \"../../components/ui/alert\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { cn } from \"../../lib/utils\";\n\nexport function meta() {\n return [{ title: \"Metrics — Dispatch\" }];\n}\n\ninterface UsageMetricBucket {\n key: string;\n label: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n inputTokens: number;\n outputTokens: number;\n activeUsers: number;\n lastActiveAt: number | null;\n}\n\ninterface 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\ninterface AppAccessMetric {\n id: string;\n name: string;\n path: string;\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n isDispatch: boolean;\n accessLabel: string;\n accessUsers: number;\n usersWithUsage: number;\n usageCalls: number;\n chatCalls: number;\n costCents: number;\n lastActiveAt: number | null;\n}\n\ninterface DailyUsageMetric {\n date: string;\n costCents: number;\n calls: number;\n chatCalls: number;\n activeUsers: number;\n}\n\ninterface 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 costCents: number;\n}\n\ninterface UsageBillingMode {\n unit: \"usd\" | \"builder-credits\";\n label: string;\n shortLabel: string;\n source: \"estimated-provider-cost\" | \"builder-agent-credits\";\n hardCostMarginMultiplier?: number;\n creditsPerUsd?: number;\n}\n\ninterface DispatchUsageMetrics {\n billing?: UsageBillingMode;\n sinceDays: 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\nconst RANGES = [7, 30, 90] as const;\n\nconst USD_BILLING: UsageBillingMode = {\n unit: \"usd\",\n label: \"Estimated spend\",\n shortLabel: \"Cost\",\n source: \"estimated-provider-cost\",\n};\n\nfunction displayAmountFromCostCents(\n cents: number,\n billing: UsageBillingMode,\n): number {\n if (billing.unit !== \"builder-credits\") return cents;\n const margin = billing.hardCostMarginMultiplier ?? 1.25;\n const creditsPerUsd = billing.creditsPerUsd ?? 20;\n const credits = (cents / 100) * margin * creditsPerUsd;\n return credits <= 0 ? 0 : Math.ceil(credits * 1000) / 1000;\n}\n\nfunction formatCredits(credits: number): string {\n if (!Number.isFinite(credits) || credits === 0) return \"0 credits\";\n const maximumFractionDigits = credits < 1 ? 3 : credits < 10 ? 2 : 1;\n const value = credits.toLocaleString(undefined, {\n maximumFractionDigits,\n });\n return `${value} ${credits === 1 ? \"credit\" : \"credits\"}`;\n}\n\nfunction formatSpend(cents: number, billing: UsageBillingMode): string {\n if (billing.unit === \"builder-credits\") {\n return formatCredits(displayAmountFromCostCents(cents, billing));\n }\n if (!Number.isFinite(cents) || cents === 0) return \"$0.00\";\n if (Math.abs(cents) < 1) return `${cents.toFixed(3)}¢`;\n if (Math.abs(cents) < 100) return `${cents.toFixed(2)}¢`;\n return (cents / 100).toLocaleString(undefined, {\n style: \"currency\",\n currency: \"USD\",\n maximumFractionDigits: 2,\n });\n}\n\nfunction formatNumber(value: number): string {\n return new Intl.NumberFormat(undefined, {\n notation: value >= 10_000 ? \"compact\" : \"standard\",\n maximumFractionDigits: value >= 10_000 ? 1 : 0,\n }).format(value);\n}\n\nfunction formatTokens(value: number): string {\n return new Intl.NumberFormat(undefined, {\n notation: \"compact\",\n maximumFractionDigits: 1,\n }).format(value);\n}\n\nfunction timeAgo(timestamp: number | null): string {\n if (!timestamp) return \"No activity\";\n const diff = Date.now() - timestamp;\n if (diff < 60_000) return \"just now\";\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;\n return `${Math.floor(diff / 86_400_000)}d ago`;\n}\n\nfunction displayApp(value: string | null | undefined): string {\n const trimmed = value?.trim();\n if (!trimmed || trimmed === \"unattributed\") return \"Unattributed\";\n return trimmed;\n}\n\nfunction maxSpend(\n rows: Array<{ costCents: number }>,\n billing: UsageBillingMode,\n): number {\n return rows.reduce(\n (max, row) =>\n Math.max(max, displayAmountFromCostCents(row.costCents, billing)),\n 0,\n );\n}\n\nfunction barWidth(value: number, max: number): string {\n if (max <= 0 || value <= 0) return \"0%\";\n return `${Math.max(4, Math.round((value / max) * 100))}%`;\n}\n\nfunction RangeSelector({\n value,\n onChange,\n}: {\n value: number;\n onChange: (value: number) => void;\n}) {\n return (\n <div className=\"flex rounded-md bg-card p-0.5\">\n {RANGES.map((range) => (\n <Button\n key={range}\n type=\"button\"\n variant={value === range ? \"secondary\" : \"ghost\"}\n size=\"sm\"\n className=\"h-7 px-3 text-xs\"\n onClick={() => onChange(range)}\n >\n {range}d\n </Button>\n ))}\n </div>\n );\n}\n\nfunction MetricCard({\n label,\n value,\n detail,\n icon,\n}: {\n label: string;\n value: string;\n detail: string;\n icon: ReactNode;\n}) {\n return (\n <div className=\"rounded-lg bg-card p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </span>\n <span className=\"text-muted-foreground\">{icon}</span>\n </div>\n <div className=\"text-2xl font-semibold tabular-nums text-foreground\">\n {value}\n </div>\n <div className=\"mt-1 truncate text-xs text-muted-foreground\">\n {detail}\n </div>\n </div>\n );\n}\n\nfunction Panel({\n title,\n icon,\n children,\n action,\n}: {\n title: string;\n icon: ReactNode;\n children: ReactNode;\n action?: ReactNode;\n}) {\n return (\n <section className=\"rounded-lg bg-card\">\n <div className=\"flex items-center justify-between gap-3 border-b px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"text-muted-foreground\">{icon}</span>\n <h2 className=\"truncate text-sm font-semibold text-foreground\">\n {title}\n </h2>\n </div>\n {action}\n </div>\n <div className=\"p-4\">{children}</div>\n </section>\n );\n}\n\nfunction LoadingMetrics() {\n return (\n <div className=\"space-y-4\">\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-5\">\n {Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg bg-card p-4\">\n <Skeleton className=\"mb-4 h-4 w-24\" />\n <Skeleton className=\"h-7 w-20\" />\n <Skeleton className=\"mt-3 h-3 w-28\" />\n </div>\n ))}\n </div>\n <Skeleton className=\"h-80 rounded-lg\" />\n </div>\n );\n}\n\nfunction AppSpendRows({\n rows,\n billing,\n}: {\n rows: UsageMetricBucket[];\n billing: UsageBillingMode;\n}) {\n const max = maxSpend(rows, billing);\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No LLM usage recorded for this window.\n </div>\n );\n }\n return (\n <div className=\"space-y-3\">\n {rows.map((row) => (\n <div key={row.key} className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-sm\">\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium text-foreground\">\n {displayApp(row.key)}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatNumber(row.chatCalls)} chats ·{\" \"}\n {formatNumber(row.activeUsers)} users\n </div>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"font-medium tabular-nums text-foreground\">\n {formatSpend(row.costCents, billing)}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatNumber(row.calls)} calls\n </div>\n </div>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-foreground\"\n style={{\n width: barWidth(\n displayAmountFromCostCents(row.costCents, billing),\n max,\n ),\n }}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction DailyActivity({ rows }: { rows: DailyUsageMetric[] }) {\n const max = Math.max(\n 1,\n rows.reduce((value, row) => Math.max(value, row.calls), 0),\n );\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No activity in this window.\n </div>\n );\n }\n return (\n <div className=\"flex h-44 items-end gap-1\">\n {rows.map((row) => (\n <div\n key={row.date}\n className=\"group flex min-w-0 flex-1 flex-col items-center gap-2\"\n >\n <div className=\"relative flex h-36 w-full items-end rounded-sm bg-muted/60\">\n <div\n className=\"w-full rounded-sm bg-foreground transition group-hover:bg-primary\"\n style={{ height: `${Math.max(4, (row.calls / max) * 100)}%` }}\n />\n </div>\n <div className=\"hidden text-[10px] text-muted-foreground sm:block\">\n {row.date.slice(5)}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction AppAccessTable({\n rows,\n billing,\n}: {\n rows: AppAccessMetric[];\n billing: UsageBillingMode;\n}) {\n const visibleRows = rows.filter((row) => !row.isDispatch);\n if (visibleRows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No workspace apps discovered yet.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[720px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">App</th>\n <th className=\"px-2 py-2 font-medium\">Access</th>\n <th className=\"px-2 py-2 text-right font-medium\">Users</th>\n <th className=\"px-2 py-2 text-right font-medium\">Chats</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n <th className=\"px-2 py-2 text-right font-medium\">Last activity</th>\n </tr>\n </thead>\n <tbody>\n {visibleRows.map((row) => (\n <tr key={row.id} className=\"border-b last:border-0\">\n <td className=\"px-2 py-3\">\n <div className=\"font-medium text-foreground\">{row.name}</div>\n <div className=\"font-mono text-[11px] text-muted-foreground\">\n {row.path}\n </div>\n </td>\n <td className=\"px-2 py-3\">\n <Badge\n variant=\"outline\"\n className={cn(\n row.status === \"pending\" &&\n \"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\",\n )}\n >\n {row.status === \"pending\"\n ? row.statusLabel || \"Builder branch\"\n : row.accessLabel}\n </Badge>\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.usersWithUsage)} /{\" \"}\n {formatNumber(row.accessUsers)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatCalls)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n <td className=\"px-2 py-3 text-right text-muted-foreground\">\n {timeAgo(row.lastActiveAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction UserTable({\n rows,\n billing,\n}: {\n rows: UserUsageMetric[];\n billing: UsageBillingMode;\n}) {\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No users have triggered LLM usage in this window.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[760px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">User</th>\n <th className=\"px-2 py-2 font-medium\">Role</th>\n <th className=\"px-2 py-2 font-medium\">Top app</th>\n <th className=\"px-2 py-2 text-right font-medium\">Chats</th>\n <th className=\"px-2 py-2 text-right font-medium\">Threads</th>\n <th className=\"px-2 py-2 text-right font-medium\">Tokens</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.slice(0, 12).map((row) => (\n <tr key={row.ownerEmail} className=\"border-b last:border-0\">\n <td className=\"max-w-64 px-2 py-3\">\n <div className=\"truncate font-medium text-foreground\">\n {row.ownerEmail}\n </div>\n <div className=\"text-muted-foreground\">\n {timeAgo(row.lastActiveAt ?? row.lastChatAt)}\n </div>\n </td>\n <td className=\"px-2 py-3\">\n <Badge variant=\"secondary\">{row.role ?? \"user\"}</Badge>\n </td>\n <td className=\"px-2 py-3 text-muted-foreground\">\n {displayApp(row.topApp)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatCalls)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatNumber(row.chatThreads)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatTokens(row.inputTokens + row.outputTokens)}\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction CompactBreakdown({\n rows,\n empty,\n billing,\n}: {\n rows: UsageMetricBucket[];\n empty: string;\n billing: UsageBillingMode;\n}) {\n const max = maxSpend(rows, billing);\n if (rows.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{empty}</div>;\n }\n return (\n <div className=\"space-y-3\">\n {rows.slice(0, 6).map((row) => (\n <div key={row.key} className=\"space-y-1\">\n <div className=\"flex items-center justify-between gap-3 text-xs\">\n <span className=\"truncate font-medium text-foreground\">\n {row.label}\n </span>\n <span className=\"shrink-0 tabular-nums text-muted-foreground\">\n {formatSpend(row.costCents, billing)}\n </span>\n </div>\n <div className=\"h-1.5 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-muted-foreground\"\n style={{\n width: barWidth(\n displayAmountFromCostCents(row.costCents, billing),\n max,\n ),\n }}\n />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction RecentTable({\n rows,\n billing,\n}: {\n rows: RecentUsageMetric[];\n billing: UsageBillingMode;\n}) {\n if (rows.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground\">\n No recent LLM calls.\n </div>\n );\n }\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[760px] text-left text-xs\">\n <thead>\n <tr className=\"border-b text-muted-foreground\">\n <th className=\"px-2 py-2 font-medium\">When</th>\n <th className=\"px-2 py-2 font-medium\">User</th>\n <th className=\"px-2 py-2 font-medium\">App</th>\n <th className=\"px-2 py-2 font-medium\">Label</th>\n <th className=\"px-2 py-2 font-medium\">Model</th>\n <th className=\"px-2 py-2 text-right font-medium\">\n {billing.shortLabel}\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.slice(0, 10).map((row) => (\n <tr key={row.id} className=\"border-b last:border-0\">\n <td className=\"px-2 py-3 text-muted-foreground\">\n {timeAgo(row.createdAt)}\n </td>\n <td className=\"max-w-56 px-2 py-3\">\n <div className=\"truncate text-foreground\">{row.ownerEmail}</div>\n </td>\n <td className=\"px-2 py-3 text-muted-foreground\">\n {displayApp(row.app)}\n </td>\n <td className=\"px-2 py-3\">\n <Badge variant=\"outline\">{row.label}</Badge>\n </td>\n <td className=\"max-w-48 px-2 py-3\">\n <div className=\"truncate text-muted-foreground\">\n {row.model}\n </div>\n </td>\n <td className=\"px-2 py-3 text-right tabular-nums\">\n {formatSpend(row.costCents, billing)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nexport default function MetricsRoute() {\n const t = useT();\n const [sinceDays, setSinceDays] = useState(30);\n const { data, isLoading, error } = useActionQuery(\n \"list-dispatch-usage-metrics\",\n { sinceDays },\n );\n const metrics = data as DispatchUsageMetrics | undefined;\n const billing = metrics?.billing ?? USD_BILLING;\n const totalTokens = useMemo(() => {\n if (!metrics) return 0;\n return (\n metrics.totals.inputTokens +\n metrics.totals.outputTokens +\n metrics.totals.cacheReadTokens +\n metrics.totals.cacheWriteTokens\n );\n }, [metrics]);\n\n return (\n <DispatchShell\n title={t(\"dispatch.nav.metrics\")}\n description={\n billing.unit === \"builder-credits\"\n ? t(\"dispatch.pages.metricsDescriptionBuilder\")\n : t(\"dispatch.pages.metricsDescriptionLlm\")\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"text-sm text-muted-foreground\">\n {metrics?.access.scope === \"organization\"\n ? `${metrics.access.totalUsers} workspace users`\n : `${metrics?.access.totalUsers ?? 0} signed-in users`}\n </div>\n <RangeSelector value={sinceDays} onChange={setSinceDays} />\n </div>\n\n {error ? (\n <Alert variant=\"destructive\">\n <IconAlertTriangle className=\"h-4 w-4\" />\n <AlertTitle>{t(\"dispatch.pages.metricsUnavailable\")}</AlertTitle>\n <AlertDescription>\n {error instanceof Error\n ? error.message\n : t(\"dispatch.pages.unableToLoadUsage\")}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {isLoading && !metrics ? <LoadingMetrics /> : null}\n\n {metrics ? (\n <>\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-5\">\n <MetricCard\n label={billing.label}\n value={formatSpend(metrics.totals.costCents, billing)}\n detail={`${formatTokens(totalTokens)} total tokens`}\n icon={<IconCoin size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.llmCalls\")}\n value={formatNumber(metrics.totals.calls)}\n detail={`${formatNumber(metrics.totals.chatCalls)} chat turns`}\n icon={<IconActivity size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.activeUsers\")}\n value={formatNumber(metrics.totals.activeUsers)}\n detail={`${formatNumber(metrics.access.totalUsers)} users with access`}\n icon={<IconUsersGroup size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.workspaceAppsStat\")}\n value={formatNumber(metrics.totals.workspaceApps)}\n detail={`${formatNumber(metrics.byApp.length)} with usage`}\n icon={<IconApps size={17} />}\n />\n <MetricCard\n label={t(\"dispatch.pages.chatThreads\")}\n value={formatNumber(metrics.totals.chatThreads)}\n detail={`${formatNumber(metrics.totals.chatMessages)} messages`}\n icon={<IconMessages size={17} />}\n />\n </div>\n\n <div className=\"grid gap-4 xl:grid-cols-[minmax(0,1.35fr)_minmax(320px,0.65fr)]\">\n <Panel\n title={\n billing.unit === \"builder-credits\"\n ? \"Credit Spend By App\"\n : \"Spend By App\"\n }\n icon={<IconChartBar size={16} />}\n >\n <AppSpendRows rows={metrics.byApp} billing={billing} />\n </Panel>\n <Panel title=\"Daily Activity\" icon={<IconClockHour4 size={16} />}>\n <DailyActivity rows={metrics.daily} />\n </Panel>\n </div>\n\n <Panel title=\"Access By App\" icon={<IconApps size={16} />}>\n <AppAccessTable rows={metrics.appAccess} billing={billing} />\n </Panel>\n\n <Panel title=\"Users\" icon={<IconUsersGroup size={16} />}>\n <UserTable rows={metrics.byUser} billing={billing} />\n </Panel>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Panel title=\"Models\" icon={<IconChartBar size={16} />}>\n <CompactBreakdown\n rows={metrics.byModel}\n empty=\"No model usage in this window.\"\n billing={billing}\n />\n </Panel>\n <Panel title=\"Work Types\" icon={<IconActivity size={16} />}>\n <CompactBreakdown\n rows={metrics.byLabel}\n empty=\"No labeled usage in this window.\"\n billing={billing}\n />\n </Panel>\n </div>\n\n <Panel title=\"Recent LLM Calls\" icon={<IconActivity size={16} />}>\n <RecentTable rows={metrics.recent} billing={billing} />\n </Panel>\n </>\n ) : null}\n </div>\n </DispatchShell>\n );\n}\n"]}
|
|
@@ -66,7 +66,7 @@ function ResultCard({ result, selected, onSelect, }) {
|
|
|
66
66
|
}
|
|
67
67
|
function MessageBlock({ message }) {
|
|
68
68
|
const tools = toolParts(message);
|
|
69
|
-
return (_jsxs("div", { className: "rounded-lg
|
|
69
|
+
return (_jsxs("div", { className: "rounded-lg bg-card", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Badge, { variant: message.role === "assistant" ? "default" : "secondary", children: message.role }), _jsx("span", { className: "truncate text-sm font-medium text-foreground", children: messageTitle(message) })] }), _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [message.attachments.length > 0 ? (_jsxs(Badge, { variant: "outline", children: [message.attachments.length, " files"] })) : null, _jsx("span", { children: formatDate(message.createdAt) })] })] }), _jsxs("div", { className: "space-y-3 px-3 py-3", children: [message.text ? (_jsx("div", { className: "whitespace-pre-wrap break-words text-sm leading-relaxed text-foreground", children: message.text })) : (_jsx("div", { className: "text-sm text-muted-foreground", children: "No text content" })), tools.length > 0 ? (_jsx("div", { className: "space-y-2", children: tools.map((tool, index) => (_jsxs("details", { className: "rounded-md border bg-muted/30 px-3 py-2", children: [_jsx("summary", { className: "cursor-pointer text-xs font-medium text-foreground", children: tool.toolName ?? tool.name ?? "tool-call" }), _jsx(RawBlock, { value: tool, className: "mt-2 max-h-72" })] }, `${message.id ?? message.index}-tool-${index}`))) })) : null] })] }));
|
|
70
70
|
}
|
|
71
71
|
function ThreadDetail({ detail }) {
|
|
72
72
|
const rawBundle = useMemo(() => ({
|
|
@@ -82,7 +82,7 @@ function ThreadDetail({ detail }) {
|
|
|
82
82
|
evals: detail.evals,
|
|
83
83
|
checkpoints: detail.checkpoints,
|
|
84
84
|
}), [detail]);
|
|
85
|
-
return (_jsxs("div", { className: "rounded-lg
|
|
85
|
+
return (_jsxs("div", { className: "rounded-lg bg-card", children: [_jsxs("div", { className: "border-b px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-base font-semibold text-foreground", children: detail.thread.title || detail.thread.preview || detail.thread.id }), _jsx("div", { className: "mt-1 truncate font-mono text-xs text-muted-foreground", children: detail.thread.id })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [detail.messages.length, " messages"] }), _jsxs(Badge, { variant: "secondary", children: [detail.runs.length, " runs"] }), _jsx(Badge, { variant: "outline", children: detail.source.label })] })] }), _jsxs("div", { className: "mt-3 grid gap-2 text-xs text-muted-foreground sm:grid-cols-3", children: [_jsxs("div", { className: "truncate", children: ["Owner: ", detail.thread.ownerEmail] }), _jsxs("div", { children: ["Created: ", formatDate(detail.thread.createdAt)] }), _jsxs("div", { children: ["Updated: ", formatDate(detail.thread.updatedAt)] })] })] }), _jsxs(Tabs, { defaultValue: "transcript", className: "p-4", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "transcript", children: "Transcript" }), _jsx(TabsTrigger, { value: "runs", children: "Runs" }), _jsx(TabsTrigger, { value: "internals", children: "Internals" }), _jsx(TabsTrigger, { value: "raw", children: "Raw" })] }), _jsx(TabsContent, { value: "transcript", className: "mt-4 space-y-3", children: detail.messages.length > 0 ? (detail.messages.map((message) => (_jsx(MessageBlock, { message: message }, message.id ?? `message-${message.index}`)))) : (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: "No persisted messages." })) }), _jsx(TabsContent, { value: "runs", className: "mt-4 space-y-3", children: detail.runs.length > 0 ? (detail.runs.map((run) => (_jsxs("details", { className: "rounded-lg bg-card", children: [_jsx("summary", { className: "cursor-pointer px-4 py-3", children: _jsxs("div", { className: "inline-flex flex-wrap items-center gap-2", children: [_jsx(Badge, { variant: "outline", children: run.status }), _jsx("span", { className: "font-mono text-xs text-foreground", children: run.id }), _jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(run.startedAt) })] }) }), _jsxs("div", { className: "space-y-2 border-t px-4 py-3", children: [run.events.map((event) => (_jsxs("details", { className: "rounded-md border bg-muted/30 px-3 py-2", children: [_jsxs("summary", { className: "cursor-pointer text-xs font-medium text-foreground", children: ["#", event.seq, " ", eventLabel(event.event)] }), _jsx(RawBlock, { value: event.event, className: "mt-2 max-h-72" })] }, `${run.id}-${event.seq}`))), run.events.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "No retained run events." })) : null] })] }, run.id)))) : (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: "No retained runs." })) }), _jsx(TabsContent, { value: "internals", className: "mt-4 space-y-4", children: _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium text-foreground", children: "Debug Runs" }), _jsx(RawBlock, { value: detail.debugRuns.length > 0
|
|
86
86
|
? detail.debugRuns
|
|
87
87
|
: (detail.debug ?? {}) })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium text-foreground", children: "Trace Summaries" }), _jsx(RawBlock, { value: detail.traces.summaries })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium text-foreground", children: "Trace Spans" }), _jsx(RawBlock, { value: detail.traces.spans })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium text-foreground", children: "Feedback And Evals" }), _jsx(RawBlock, { value: {
|
|
88
88
|
feedback: detail.feedback,
|
|
@@ -146,7 +146,7 @@ export default function ThreadDebugRoute() {
|
|
|
146
146
|
}),
|
|
147
147
|
}).catch(() => { });
|
|
148
148
|
}, [ownerEmail, query, selected?.threadId, sourceId, threadId]);
|
|
149
|
-
return (_jsx(DispatchShell, { title: "Thread Debug", description: "Inspect persisted agent chat threads, run events, and AI internals.", children: _jsxs("div", { className: "space-y-4", children: [sourcesQuery.isError ? (_jsx(ActionQueryError, { error: sourcesQuery.error, onRetry: () => void sourcesQuery.refetch() })) : null, _jsxs("section", { className: "rounded-lg
|
|
149
|
+
return (_jsx(DispatchShell, { title: "Thread Debug", description: "Inspect persisted agent chat threads, run events, and AI internals.", children: _jsxs("div", { className: "space-y-4", children: [sourcesQuery.isError ? (_jsx(ActionQueryError, { error: sourcesQuery.error, onRetry: () => void sourcesQuery.refetch() })) : null, _jsxs("section", { className: "rounded-lg bg-card p-4", children: [_jsxs("div", { className: "grid gap-3 lg:grid-cols-[220px_1fr_260px_auto]", children: [_jsxs(Select, { value: sourceId, onValueChange: setSourceId, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Source" }) }), _jsxs(SelectContent, { children: [sources.map((source) => (_jsx(SelectItem, { value: source.id, children: source.label }, source.id))), sources.length === 0 ? (_jsx(SelectItem, { value: "current", children: "Current Dispatch DB" })) : null] })] }), _jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search title, preview, messages, tools" }), _jsx(Input, { value: ownerEmail, onChange: (event) => setOwnerEmail(event.target.value), placeholder: "Owner email" }), _jsxs(Button, { type: "button", onClick: () => setSubmittedSearch({
|
|
150
150
|
sourceId,
|
|
151
151
|
query: query.trim(),
|
|
152
152
|
ownerEmail: ownerEmail.trim(),
|
|
@@ -159,10 +159,10 @@ export default function ThreadDebugRoute() {
|
|
|
159
159
|
threadId: trimmed,
|
|
160
160
|
ownerEmail: ownerEmail.trim() || undefined,
|
|
161
161
|
});
|
|
162
|
-
}, children: [_jsx(IconFileSearch, { size: 16 }), "Inspect"] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [sourcesLoading ? _jsx(Skeleton, { className: "h-5 w-32" }) : null, selectedSource ? _jsx(SourceBadge, { source: selectedSource }) : null, selectedSource?.databaseUrlEnv ? (_jsx(Badge, { variant: "outline", className: "font-mono", children: selectedSource.databaseUrlEnv })) : null, sourcesData?.access ? (_jsxs("span", { children: [sourcesData.access.viewerEmail, " \u00B7", " ", sourcesData.access.canInspectAll ? "admin scope" : "own scope"] })) : null] })] }), searchError ? (_jsx(ActionQueryError, { error: searchError, onRetry: () => void refetchSearch() })) : null, _jsxs("div", { className: "grid gap-4 xl:grid-cols-[380px_1fr]", children: [_jsxs("section", { className: "min-h-[520px] rounded-lg
|
|
162
|
+
}, children: [_jsx(IconFileSearch, { size: 16 }), "Inspect"] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [sourcesLoading ? _jsx(Skeleton, { className: "h-5 w-32" }) : null, selectedSource ? _jsx(SourceBadge, { source: selectedSource }) : null, selectedSource?.databaseUrlEnv ? (_jsx(Badge, { variant: "outline", className: "font-mono", children: selectedSource.databaseUrlEnv })) : null, sourcesData?.access ? (_jsxs("span", { children: [sourcesData.access.viewerEmail, " \u00B7", " ", sourcesData.access.canInspectAll ? "admin scope" : "own scope"] })) : null] })] }), searchError ? (_jsx(ActionQueryError, { error: searchError, onRetry: () => void refetchSearch() })) : null, _jsxs("div", { className: "grid gap-4 xl:grid-cols-[380px_1fr]", children: [_jsxs("section", { className: "min-h-[520px] rounded-lg bg-card", children: [_jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-foreground", children: "Threads" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [searchData?.count ?? 0, " results \u00B7", " ", searchData?.access?.scope ?? "current scope"] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => refetchSearch(), "aria-label": "Refresh threads", children: _jsx(IconRefresh, { size: 16 }) })] }), _jsxs("div", { className: "max-h-[760px] space-y-2 overflow-auto p-3", children: [searchLoading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-28 w-full rounded-lg" }), _jsx(Skeleton, { className: "h-28 w-full rounded-lg" }), _jsx(Skeleton, { className: "h-28 w-full rounded-lg" })] })) : null, !searchLoading && searchThreads.length === 0 ? (_jsxs("div", { className: "flex min-h-64 flex-col items-center justify-center rounded-lg border border-dashed px-4 text-center text-sm text-muted-foreground", children: [_jsx(IconDatabase, { className: "mb-2 h-5 w-5" }), "No threads found."] })) : null, searchThreads.map((result) => (_jsx(ResultCard, { result: result, selected: selected?.threadId === result.id, onSelect: () => setSelected({
|
|
163
163
|
sourceId: submittedSearch.sourceId,
|
|
164
164
|
threadId: result.id,
|
|
165
165
|
ownerEmail: submittedSearch.ownerEmail || undefined,
|
|
166
|
-
}) }, result.id)))] })] }), _jsxs("section", { className: "min-w-0", children: [detailError ? (_jsx(ActionQueryError, { error: detailError, onRetry: () => void refetchDetail() })) : null, detailLoading ? (_jsxs("div", { className: "rounded-lg
|
|
166
|
+
}) }, result.id)))] })] }), _jsxs("section", { className: "min-w-0", children: [detailError ? (_jsx(ActionQueryError, { error: detailError, onRetry: () => void refetchDetail() })) : null, detailLoading ? (_jsxs("div", { className: "rounded-lg bg-card p-4", children: [_jsx(Skeleton, { className: "h-6 w-72" }), _jsx(Skeleton, { className: "mt-3 h-4 w-96" }), _jsx(Skeleton, { className: "mt-6 h-[520px] w-full" })] })) : detail ? (_jsx(ThreadDetail, { detail: detail })) : (_jsxs("div", { className: "flex min-h-[520px] flex-col items-center justify-center rounded-lg border border-dashed bg-card px-4 text-center text-sm text-muted-foreground", children: [_jsx(IconFileSearch, { className: "mb-2 h-5 w-5" }), "Select or inspect a thread."] }))] })] })] }) }));
|
|
167
167
|
}
|
|
168
168
|
//# sourceMappingURL=thread-debug.js.map
|