Tuskira AI Agent Gateway 的管理控制台。React 19 + Vite + TypeScript(严格模式)+ Tailwind CSS 4 + shadcn/ui。
需要 Node 26+ 和 npm 11+(不支持 pnpm)。
npm install
npm run dev
开发服务器将 /api 代理到 http://localhost:8081(参见 vite.config.ts),因此请在 :8081 上运行网关,与 UI 并行。使用网关签发的 API 密钥(gk_…)在 /login 登录。
在仓库根目录下,你也可以改为运行:
make ui-install
make ui-dev
npm run build
将生产包输出到 dist/。在生产环境中,网关自身在 :8081 的 / 路径上提供此包,因此应用以同源方式与 API 通信——无需代理。
make ui-build # from the repo root
控制台也以 @tuskira/gateway-web 的形式打包,因此你可以将其页面挂载到你自己的 React 应用中。该包不在 npm registry 上。每个 GitHub release 都以 tarball 形式附带它,其版本始终与 gateway release 标签一致。React、React DOM、React Router(react-router-dom 7)和 TanStack Query 5 是 peer 依赖;其余所有内容随该包一起安装。
npm install https://github.com/Tuskira/ai-agent-gateway/releases/download/v<version>/tuskira-gateway-web-<version>.tgz \
react react-dom react-router-dom @tanstack/react-query
npm 会在 package-lock.json 中记录 tarball 的完整性哈希。
import {
AppShell, AuthProvider, RequireAuth, LoginPage, Toaster, TooltipProvider,
configureGateway, gatewayPageRoutes,
} from '@tuskira/gateway-web'
import '@tuskira/gateway-web/style.css' // Tailwind output + design tokens
import '@tuskira/gateway-web/fonts.css' // optional: self-hosted Inter / JetBrains Mono
configureGateway({
apiBase: '/gw/api/v1', // default '/api/v1'
mcpBase: 'https://gw.example.com/mcp', // default: guessed from the page origin
onUnauthenticated: () => router.navigate('/login'), // default: window.location.assign('/login')
})
const router = createBrowserRouter(
[
{ path: '/login', element: <LoginPage /> },
{ element: <RequireAuth />, children: [{ element: <AppShell />, children: gatewayPageRoutes() }] },
],
{ basename: '/gateway' },
)
将路由包裹在 QueryClientProvider、AuthProvider 和 TooltipProvider 中,并像 src/App.tsx 那样渲染一次 <Toaster />。如果你希望控制台拥有整个路由,gatewayRoutes() 会返回完整的路由树(登录、文档、修改密码、外壳以及兜底重定向)。仅支持 @tuskira/gateway-web 导出的内容;不要从 src/ 导入。
传入一个 extension,将你自己的侧边栏分组和页面放入控制台的外壳和登录守卫中:
gatewayRoutes({ extension: { navGroups: [...], routes: [...] } })
import { Plug } from 'lucide-react'
import { AppShell, gatewayPageRoutes, type ConsoleExtension } from '@tuskira/gateway-web'
const extension: ConsoleExtension = {
navGroups: [
// New group, placed before the core "Observe" group (appended without `before`).
{ label: 'Billing', before: 'Observe', items: [
{ label: 'Invoices', path: '/invoices', icon: Plug, description: 'Invoices and usage.' },
] },
// Same label as a core group: the items are added to that group.
{ label: 'Access', items: [
{ label: 'Audit', path: '/audit', icon: Plug, description: 'Audit trail.' },
] },
],
routes: [
{ path: '/invoices', Component: InvoicesPage },
{ path: '/audit', Component: AuditPage },
],
}
// Own router: pass the extension to both the routes and the shell.
{ element: <AppShell extension={extension} />, children: gatewayPageRoutes(extension) }
gatewayRoutes({ extension }) 会为你完成这两件事。路径没有对应路由的导航项会显示“即将推出”占位符。对于路径是其他项路径前缀的项,设置 end: true,使其仅在其自身路径上处于激活状态。
浏览器必须能够通过你设置为 apiBase 的路径访问网关 API。当宿主应用提供控制台服务时,添加一条反向代理规则,将 /api/v1/*(或你的 apiBase 路径,重写回 /api/v1)转发到端口 8081 上的网关。保持路径、所有请求和响应头(Authorization、X-CSRF-Token、X-Gateway-Acting-Tenant)以及 cookie 不变,否则登录和 CSRF 校验将会失败。
使用 npm run build:lib 构建该包,它会生成 dist-lib/(index.js、types/、style.css、fonts.css)。随后 npm pack 会生成 tarball;发布工作流会在每个 v* 标签上将其附加到 GitHub release。
npm run lint # eslint
npm run typecheck # tsc --noEmit (project references)
npm run test # vitest
npm run format:check # prettier --check
npx shadcn@latest add <component>
本项目的 components.json 面向 Vite 模板、radix 基础、neutral 基础色以及用于主题化的 CSS 变量,其中 @/* 导入别名解析为 src/*。新组件写入 src/components/ui/。
tokens.json(仓库根目录)是设计系统的唯一事实来源——颜色、间距、圆角、阴影、字体。编辑后运行 npm run gen:tokens 重新生成:
src/styles/tokens.css —— 以 CSS 自定义属性形式呈现的原始令牌(:root 用于浅色值,.dark 用于深色覆盖),保留设计师自己的令牌名称(--indigo-9、--sev-high、--r-4、--shadow-1 等)。src/styles/tokens.ts —— 相同的分类色阶(colors.indigo、colors.slate、colors.sev、colors.status、colors.brand)以及 fonts、radii、shadows,供非 CSS 使用方(例如 recharts 系列颜色)使用。src/index.css 将 shadcn 的语义变量(--background、--primary、--sidebar 等)映射到这些令牌上,并注册 Tailwind v4 的 @theme 条目,使 bg-indigo-9、text-text-muted、border-border-strong、shadow-1、rounded-r-4 和 font-mono 等工具类可在应用任意位置使用。请勿手动编辑 src/styles/tokens.css / src/styles/tokens.ts——它们会被生成器覆盖。src/test/tokens.test.ts 断言 tokens.ts 中的每个条目在 tokens.css 中都有对应的声明。
web/
├── index.html
├── vite.config.ts # React + Tailwind plugins, /api dev proxy, vitest config
├── components.json # shadcn/ui config
├── tokens.json # design-token source of truth (see Design tokens)
├── scripts/
│ └── gen-tokens.mjs # tokens.json -> src/styles/tokens.{css,ts}
├── src/
│ ├── main.tsx # ReactDOM root
│ ├── App.tsx # QueryClientProvider, AuthProvider, RouterProvider
│ ├── index.css # Tailwind entrypoint + semantic token mapping
│ ├── styles/
│ │ ├── tokens.css # generated — raw design tokens as CSS vars
│ │ └── tokens.ts # generated — same tokens for non-CSS use
│ ├── auth/
│ │ ├── AuthContext.tsx # AuthProvider / useAuth — principal, signIn, signOut
│ │ └── RequireAuth.tsx # route guard for the authenticated shell
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives (generated, don't hand-edit)
│ │ ├── layout/ # AppShell, Sidebar, TopBar
│ │ └── app/ # ComingSoon, StatCard
│ ├── hooks/
│ │ └── use-theme.ts # light/dark toggle, class strategy, localStorage
│ ├── lib/
│ │ ├── api.ts # fetch wrapper: bearer auth, 401 handling, ApiError
│ │ ├── nav.ts # single source of truth for sidebar nav + routes
│ │ ├── queries.ts # react-query hooks (useHealth)
│ │ ├── query-client.ts # QueryClient instance
│ │ ├── types.ts # Principal, HealthResponse, ApiErrorBody
│ │ └── utils.ts # cn() (shadcn)
│ ├── routes/
│ │ ├── router.tsx # createBrowserRouter (data router)
│ │ ├── LoginPage.tsx
│ │ ├── OverviewPage.tsx
│ │ └── DocsPage.tsx
│ └── test/
│ ├── setup.ts
│ ├── LoginPage.test.tsx
│ ├── Sidebar.test.tsx
│ ├── SignOut.test.tsx
│ └── tokens.test.ts