@splyntra/dashboard 0.3.0
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/next.config.js +33 -0
- package/package.json +62 -0
- package/postcss.config.js +7 -0
- package/public/manifest.json +9 -0
- package/src/app/accept-invite/page.tsx +43 -0
- package/src/app/agents/layout.tsx +11 -0
- package/src/app/agents/page.tsx +149 -0
- package/src/app/alerts/page.tsx +227 -0
- package/src/app/api/auth/[...nextauth]/route.ts +4 -0
- package/src/app/api/eval/[...path]/route.ts +61 -0
- package/src/app/api/v1/[...path]/route.ts +87 -0
- package/src/app/auth-actions.ts +103 -0
- package/src/app/costs/layout.tsx +11 -0
- package/src/app/costs/page.tsx +155 -0
- package/src/app/evaluations/page.tsx +135 -0
- package/src/app/globals.css +42 -0
- package/src/app/layout.tsx +26 -0
- package/src/app/login/page.tsx +52 -0
- package/src/app/metrics/page.tsx +148 -0
- package/src/app/not-found.tsx +23 -0
- package/src/app/page.tsx +56 -0
- package/src/app/projects/page.tsx +130 -0
- package/src/app/providers.tsx +33 -0
- package/src/app/settings/keys/page.tsx +174 -0
- package/src/app/settings/team/InviteForm.tsx +44 -0
- package/src/app/settings/team/page.tsx +112 -0
- package/src/app/signup/page.tsx +33 -0
- package/src/app/traces/[traceId]/page.tsx +132 -0
- package/src/app/traces/layout.tsx +11 -0
- package/src/app/traces/page.tsx +31 -0
- package/src/auth.config.ts +60 -0
- package/src/auth.ts +54 -0
- package/src/components/auth/AuthCard.tsx +45 -0
- package/src/components/layout/AppShell.tsx +22 -0
- package/src/components/layout/Sidebar.tsx +177 -0
- package/src/components/trace/TraceList.tsx +81 -0
- package/src/components/trace/TraceViewer.test.tsx +82 -0
- package/src/components/trace/TraceViewer.tsx +237 -0
- package/src/components/ui/ErrorBoundary.tsx +57 -0
- package/src/components/ui/Skeleton.tsx +40 -0
- package/src/components/ui/primitives.tsx +171 -0
- package/src/global.d.ts +1 -0
- package/src/lib/api.test.ts +24 -0
- package/src/lib/api.ts +379 -0
- package/src/lib/auth-extensions.ts +47 -0
- package/src/lib/auth-providers.ts +8 -0
- package/src/lib/collector-auth-providers.ts +8 -0
- package/src/lib/collector-auth.ts +52 -0
- package/src/lib/db.ts +27 -0
- package/src/lib/features.ts +19 -0
- package/src/lib/hooks.ts +116 -0
- package/src/lib/project-context.tsx +47 -0
- package/src/lib/slots.ts +50 -0
- package/src/middleware.ts +12 -0
- package/src/types/trace.ts +55 -0
- package/tailwind.config.js +26 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
import { createContext, useContext, useEffect, useState, useCallback } from "react";
|
|
5
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
6
|
+
import { getActiveProject, setActiveProject } from "@/lib/api";
|
|
7
|
+
|
|
8
|
+
interface ProjectContextValue {
|
|
9
|
+
/** Active project id, or "" for the API key's default project. */
|
|
10
|
+
projectId: string;
|
|
11
|
+
setProjectId: (id: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ProjectContext = createContext<ProjectContextValue>({
|
|
15
|
+
projectId: "",
|
|
16
|
+
setProjectId: () => {},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export function ProjectProvider({ children }: { children: React.ReactNode }) {
|
|
20
|
+
const [projectId, setProjectIdState] = useState("");
|
|
21
|
+
const queryClient = useQueryClient();
|
|
22
|
+
|
|
23
|
+
// Hydrate from localStorage on mount (avoids SSR mismatch).
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setProjectIdState(getActiveProject());
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const setProjectId = useCallback(
|
|
29
|
+
(id: string) => {
|
|
30
|
+
setActiveProject(id);
|
|
31
|
+
setProjectIdState(id);
|
|
32
|
+
// Switching project changes the scope of every query — refetch all.
|
|
33
|
+
queryClient.invalidateQueries();
|
|
34
|
+
},
|
|
35
|
+
[queryClient]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ProjectContext.Provider value={{ projectId, setProjectId }}>
|
|
40
|
+
{children}
|
|
41
|
+
</ProjectContext.Provider>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function useProject(): ProjectContextValue {
|
|
46
|
+
return useContext(ProjectContext);
|
|
47
|
+
}
|
package/src/lib/slots.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Extension slots for the dashboard. The open build registers nothing, so these
|
|
3
|
+
// are empty; the private frontend/cloud-screens package calls registerNavItem
|
|
4
|
+
// (from a module imported by the cloud web build) to contribute nav entries for
|
|
5
|
+
// its screens. The screens' page files are composed into the app/ route tree at
|
|
6
|
+
// cloud-build time — see the cloud-screens README.
|
|
7
|
+
//
|
|
8
|
+
// This keeps one dashboard codebase: the open viewer is fully usable alone, and
|
|
9
|
+
// commercial screens mount in without the open repo importing any private code.
|
|
10
|
+
|
|
11
|
+
export interface NavSlotItem {
|
|
12
|
+
href: string;
|
|
13
|
+
label: string;
|
|
14
|
+
/** lucide-react icon name, resolved by the Sidebar against its icon map. */
|
|
15
|
+
icon: string;
|
|
16
|
+
/** Optional feature flag that must be enabled for this item to render. */
|
|
17
|
+
feature?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const navItems: NavSlotItem[] = [];
|
|
21
|
+
|
|
22
|
+
/** Register a sidebar nav item. Called by commercial screen packages. */
|
|
23
|
+
export function registerNavItem(item: NavSlotItem): void {
|
|
24
|
+
if (!navItems.some((i) => i.href === item.href)) {
|
|
25
|
+
navItems.push(item);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Nav items contributed by extension slots (empty in OSS). */
|
|
30
|
+
export function navSlotItems(): readonly NavSlotItem[] {
|
|
31
|
+
return navItems;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Widget slots: locations where a commercial build can mount a React component
|
|
35
|
+
// (e.g. the org switcher in the sidebar). Empty in OSS.
|
|
36
|
+
import type { ComponentType } from "react";
|
|
37
|
+
|
|
38
|
+
export type WidgetSlot = "sidebarTop";
|
|
39
|
+
|
|
40
|
+
const widgets: Record<WidgetSlot, ComponentType[]> = { sidebarTop: [] };
|
|
41
|
+
|
|
42
|
+
/** Mount a component into a named widget slot. Called by commercial packages. */
|
|
43
|
+
export function registerWidget(slot: WidgetSlot, component: ComponentType): void {
|
|
44
|
+
widgets[slot].push(component);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Components contributed to a widget slot (empty in OSS). */
|
|
48
|
+
export function slotWidgets(slot: WidgetSlot): readonly ComponentType[] {
|
|
49
|
+
return widgets[slot];
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
import NextAuth from "next-auth";
|
|
3
|
+
import { authConfig } from "@/auth.config";
|
|
4
|
+
|
|
5
|
+
// Edge middleware uses the DB-free config to gate routes (the `authorized`
|
|
6
|
+
// callback). Full credential verification happens in the Node auth instance.
|
|
7
|
+
export const { auth: middleware } = NextAuth(authConfig);
|
|
8
|
+
|
|
9
|
+
export const config = {
|
|
10
|
+
// Protect everything except Next internals and static assets.
|
|
11
|
+
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
|
|
12
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Trace types - the core data model surfaced in the dashboard.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Trace {
|
|
7
|
+
traceId: string;
|
|
8
|
+
agentId: string;
|
|
9
|
+
workflowId?: string;
|
|
10
|
+
status: "ok" | "error";
|
|
11
|
+
latencyMs: number;
|
|
12
|
+
totalTokens: number;
|
|
13
|
+
costUsd: number;
|
|
14
|
+
riskScore: number;
|
|
15
|
+
riskSeverity: "NONE" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
|
|
16
|
+
detections: Detection[];
|
|
17
|
+
spans: Span[];
|
|
18
|
+
startedAt: string;
|
|
19
|
+
completedAt: string;
|
|
20
|
+
orgId: string;
|
|
21
|
+
projectId: string;
|
|
22
|
+
environment: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Span {
|
|
26
|
+
spanId: string;
|
|
27
|
+
parentSpanId?: string;
|
|
28
|
+
type: "agent" | "llm_call" | "tool_call" | "step";
|
|
29
|
+
name: string;
|
|
30
|
+
status: "ok" | "error";
|
|
31
|
+
latencyMs: number;
|
|
32
|
+
tokens?: TokenUsage;
|
|
33
|
+
input?: Record<string, unknown>;
|
|
34
|
+
output?: Record<string, unknown>;
|
|
35
|
+
detections: Detection[];
|
|
36
|
+
startedAt: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface TokenUsage {
|
|
40
|
+
promptTokens: number;
|
|
41
|
+
completionTokens: number;
|
|
42
|
+
totalTokens: number;
|
|
43
|
+
model: string;
|
|
44
|
+
costUsd: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Detection {
|
|
48
|
+
detector: "pii" | "secrets" | "injection";
|
|
49
|
+
category: string;
|
|
50
|
+
severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
|
|
51
|
+
confidence: number;
|
|
52
|
+
description: string;
|
|
53
|
+
beta: boolean;
|
|
54
|
+
spanId?: string;
|
|
55
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/** @type {import('tailwindcss').Config} */
|
|
3
|
+
module.exports = {
|
|
4
|
+
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
|
|
5
|
+
theme: {
|
|
6
|
+
extend: {
|
|
7
|
+
colors: {
|
|
8
|
+
splyntra: {
|
|
9
|
+
50: "#f0f4ff",
|
|
10
|
+
100: "#dbe4ff",
|
|
11
|
+
500: "#4c6ef5",
|
|
12
|
+
600: "#3b5bdb",
|
|
13
|
+
700: "#364fc7",
|
|
14
|
+
900: "#1c2541",
|
|
15
|
+
},
|
|
16
|
+
risk: {
|
|
17
|
+
low: "#51cf66",
|
|
18
|
+
medium: "#fcc419",
|
|
19
|
+
high: "#ff6b6b",
|
|
20
|
+
critical: "#c92a2a",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
plugins: [require("@tailwindcss/typography")],
|
|
26
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
+
"exclude": [
|
|
23
|
+
"node_modules",
|
|
24
|
+
"vitest.config.ts",
|
|
25
|
+
"vitest.setup.ts",
|
|
26
|
+
"src/**/*.test.ts",
|
|
27
|
+
"src/**/*.test.tsx"
|
|
28
|
+
]
|
|
29
|
+
}
|