feat(web): new "运行状态" page (new-model agent status, not old task fields)
Re-add a run-status menu (I shouldn't have removed it) but showing ONLY what the
template-agent model actually has — no old 待确认/sub_agile/子任务流/智能体任务图/
产物/SK快照/合并时间线. New /agent-status page:
- summary counts (total / running / starting / other)
- agent list (template Chinese name, live status badge, subdomain, #resources)
- detail panel: live status (polls /agents/{id}/status from AM), copyable
subdomain, mounted resources (names from /api/resources), runtime_id, timestamps.
Sidebar item "运行状态" + zh key added; build + tsc clean; route registered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Vendored
+1
-1
@@ -20,7 +20,7 @@
|
||||
<meta property="og:type" content="website" />
|
||||
|
||||
<meta name="theme-color" content="#7B6BE3" />
|
||||
<link rel="icon" href="/favicon.ico"><script defer src="/static/js/vendor-radix.829c7e3fad.js"></script><script defer src="/static/js/vendor-tanstack.632dbe8908.js"></script><script defer src="/static/js/lib-react.5c8909c28c.js"></script><script defer src="/static/js/6374.bc21d8b214.js"></script><script defer src="/static/js/index.046d6d6bc8.js"></script><link href="/static/css/index.48ece32abf.css" rel="stylesheet"></head>
|
||||
<link rel="icon" href="/favicon.ico"><script defer src="/static/js/vendor-radix.829c7e3fad.js"></script><script defer src="/static/js/vendor-tanstack.632dbe8908.js"></script><script defer src="/static/js/lib-react.5c8909c28c.js"></script><script defer src="/static/js/6374.bc21d8b214.js"></script><script defer src="/static/js/index.0ac7693b4a.js"></script><link href="/static/css/index.08eae7aeba.css" rel="stylesheet"></head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { Activity, Copy, Database, GitBranch, RefreshCw, Server, X } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { api } from '@/lib/api'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
|
||||
type AgentItem = {
|
||||
agent_id: string
|
||||
template_id: string
|
||||
subdomain: string
|
||||
binding_ids: number[]
|
||||
status: string
|
||||
runtime_id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
type TemplateItem = { template_id: string; name: string }
|
||||
type ResourceItem = { id: number; name: string; resource_type: string }
|
||||
|
||||
const RESOURCE_ICON: Record<string, typeof GitBranch> = {
|
||||
git: GitBranch,
|
||||
database: Database,
|
||||
blob: Database,
|
||||
vm: Server,
|
||||
}
|
||||
|
||||
function statusLabel(s: string): { text: string; cls: string } {
|
||||
const v = (s || '').toLowerCase()
|
||||
if (['running', 'active', 'ready'].includes(v))
|
||||
return { text: '运行中', cls: 'bg-emerald-500/15 text-emerald-300 ring-emerald-500/30' }
|
||||
if (['pending', 'starting', 'provisioning'].includes(v))
|
||||
return { text: '启动中', cls: 'bg-amber-500/15 text-amber-300 ring-amber-500/30' }
|
||||
if (['stopped'].includes(v))
|
||||
return { text: '已停止', cls: 'bg-muted/40 text-muted-foreground ring-border' }
|
||||
if (['failed', 'error', 'unhealthy', 'crashed'].includes(v))
|
||||
return { text: '异常', cls: 'bg-rose-500/15 text-rose-300 ring-rose-500/30' }
|
||||
return { text: s || '—', cls: 'bg-amber-500/15 text-amber-300 ring-amber-500/30' }
|
||||
}
|
||||
|
||||
async function listAgents(): Promise<AgentItem[]> {
|
||||
const res = await api.get<{ data?: { items?: AgentItem[] } }>('/api/heicode/agents')
|
||||
return res.data?.data?.items ?? []
|
||||
}
|
||||
async function listTemplates(): Promise<TemplateItem[]> {
|
||||
const res = await api.get<{ data?: { items?: TemplateItem[] } }>('/api/heicode/agent-templates')
|
||||
return res.data?.data?.items ?? []
|
||||
}
|
||||
async function listResources(): Promise<ResourceItem[]> {
|
||||
const res = await api.get<{ data?: { items?: ResourceItem[] } }>('/api/resources/?status=active')
|
||||
return res.data?.data?.items ?? []
|
||||
}
|
||||
|
||||
export function AgentStatusPage() {
|
||||
const qc = useQueryClient()
|
||||
const [selected, setSelected] = useState<AgentItem | null>(null)
|
||||
|
||||
const agentsQ = useQuery({ queryKey: ['agent-status', 'agents'], queryFn: listAgents, refetchInterval: 15_000, retry: false })
|
||||
const templatesQ = useQuery({ queryKey: ['agent-templates'], queryFn: listTemplates, retry: false })
|
||||
const resourcesQ = useQuery({ queryKey: ['resources', 'active'], queryFn: listResources, retry: false })
|
||||
|
||||
const templateName = (key: string) =>
|
||||
(templatesQ.data ?? []).find((t) => t.template_id === key)?.name ?? key
|
||||
const resourceName = (id: number) =>
|
||||
(resourcesQ.data ?? []).find((r) => r.id === id)?.name ?? `资源#${id}`
|
||||
const resourceType = (id: number) =>
|
||||
(resourcesQ.data ?? []).find((r) => r.id === id)?.resource_type ?? ''
|
||||
|
||||
const agents = agentsQ.data ?? []
|
||||
const counts = useMemo(() => {
|
||||
let running = 0, pending = 0, other = 0
|
||||
for (const a of agents) {
|
||||
const v = (a.status || '').toLowerCase()
|
||||
if (['running', 'active', 'ready'].includes(v)) running++
|
||||
else if (['pending', 'starting', 'provisioning'].includes(v)) pending++
|
||||
else other++
|
||||
}
|
||||
return { running, pending, other, total: agents.length }
|
||||
}, [agents])
|
||||
|
||||
const copy = async (text: string) => {
|
||||
if (!text) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
toast.success('已复制')
|
||||
} catch {
|
||||
toast.error('复制失败,请手动复制')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='space-y-6'>
|
||||
<div className='rounded-2xl border border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] bg-[color-mix(in_oklch,var(--card)_70%,transparent)] p-5'>
|
||||
<p className='text-muted-foreground text-[11px] font-semibold tracking-[0.16em] uppercase'>
|
||||
Heicode Manager
|
||||
</p>
|
||||
<h2 className='mt-1 flex items-center gap-2 text-xl font-semibold'>
|
||||
<Activity className='text-primary h-5 w-5' /> 运行状态
|
||||
</h2>
|
||||
<p className='text-muted-foreground mt-1 text-sm'>
|
||||
查看你部署的 Agent 当前运行情况。状态每 15 秒自动刷新。
|
||||
</p>
|
||||
<div className='mt-4 grid grid-cols-2 gap-3 sm:grid-cols-4'>
|
||||
<Stat label='总数' value={counts.total} />
|
||||
<Stat label='运行中' value={counts.running} tone='emerald' />
|
||||
<Stat label='启动中' value={counts.pending} tone='amber' />
|
||||
<Stat label='其它' value={counts.other} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className='rounded-2xl border border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] bg-[color-mix(in_oklch,var(--card)_70%,transparent)] p-5'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h3 className='text-base font-semibold'>Agent 列表</h3>
|
||||
<Button variant='ghost' size='sm' className='gap-1 text-xs' onClick={() => void qc.invalidateQueries({ queryKey: ['agent-status'] })}>
|
||||
<RefreshCw className='h-3.5 w-3.5' /> 刷新
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{agentsQ.isLoading ? (
|
||||
<div className='mt-4 space-y-3'>{[0, 1].map((i) => <Skeleton key={i} className='h-14 rounded-xl' />)}</div>
|
||||
) : agents.length === 0 ? (
|
||||
<div className='text-muted-foreground mt-4 rounded-xl border border-dashed p-8 text-center text-sm'>
|
||||
还没有部署 Agent。去
|
||||
<Link to='/deploy-agent' className='text-primary mx-1 underline'>部署 Sub Agent</Link>
|
||||
部署一个。
|
||||
</div>
|
||||
) : (
|
||||
<div className='mt-4 overflow-hidden rounded-xl border border-[color-mix(in_oklch,var(--primary)_18%,var(--border))]'>
|
||||
{agents.map((a, idx) => {
|
||||
const st = statusLabel(a.status)
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
key={a.agent_id}
|
||||
onClick={() => setSelected(a)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-3 p-3 text-left transition hover:bg-[color-mix(in_oklch,var(--primary)_8%,transparent)]',
|
||||
idx > 0 && 'border-t border-[color-mix(in_oklch,var(--primary)_12%,var(--border))]'
|
||||
)}
|
||||
>
|
||||
<span className={cn('inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold ring-1 ring-inset', st.cls)}>
|
||||
{st.text}
|
||||
</span>
|
||||
<span className='min-w-0 flex-1'>
|
||||
<span className='block truncate text-sm font-medium'>{templateName(a.template_id)}</span>
|
||||
<span className='text-muted-foreground block truncate font-mono text-[11px]'>{a.subdomain || '地址生成中…'}</span>
|
||||
</span>
|
||||
<span className='text-muted-foreground shrink-0 text-[11px]'>
|
||||
{(a.binding_ids?.length ?? 0) > 0 ? `挂 ${a.binding_ids.length} 个资源` : '无资源'}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{selected && (
|
||||
<DetailPanel
|
||||
agent={selected}
|
||||
templateName={templateName(selected.template_id)}
|
||||
resourceName={resourceName}
|
||||
resourceType={resourceType}
|
||||
onClose={() => setSelected(null)}
|
||||
onCopy={copy}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Stat({ label, value, tone }: { label: string; value: number; tone?: 'emerald' | 'amber' }) {
|
||||
return (
|
||||
<div className='rounded-xl border border-[color-mix(in_oklch,var(--primary)_18%,var(--border))] bg-[color-mix(in_oklch,var(--card)_55%,transparent)] p-3'>
|
||||
<p className='text-muted-foreground text-[11px] font-semibold tracking-wider uppercase'>{label}</p>
|
||||
<p className={cn('mt-1 text-2xl font-semibold tabular-nums', tone === 'emerald' && 'text-emerald-400', tone === 'amber' && 'text-amber-400')}>{value}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DetailPanel({
|
||||
agent,
|
||||
templateName,
|
||||
resourceName,
|
||||
resourceType,
|
||||
onClose,
|
||||
onCopy,
|
||||
}: {
|
||||
agent: AgentItem
|
||||
templateName: string
|
||||
resourceName: (id: number) => string
|
||||
resourceType: (id: number) => string
|
||||
onClose: () => void
|
||||
onCopy: (t: string) => void
|
||||
}) {
|
||||
// Live status pull from AM for this agent.
|
||||
const liveQ = useQuery({
|
||||
queryKey: ['agent-status', 'live', agent.agent_id],
|
||||
queryFn: async () => {
|
||||
const res = await api.get<{ data?: { status?: string } }>(`/api/heicode/agents/${agent.agent_id}/status`)
|
||||
return res.data?.data?.status ?? agent.status
|
||||
},
|
||||
refetchInterval: 10_000,
|
||||
retry: false,
|
||||
})
|
||||
const st = statusLabel(liveQ.data ?? agent.status)
|
||||
|
||||
return (
|
||||
<div className='fixed inset-y-0 right-0 z-50 w-[min(460px,96vw)] overflow-y-auto border-l border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] bg-[color-mix(in_oklch,var(--card)_92%,black)] p-5 shadow-2xl backdrop-blur'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<h3 className='text-lg font-semibold'>{templateName}</h3>
|
||||
<p className='text-muted-foreground mt-0.5 font-mono text-[11px]'>{agent.agent_id}</p>
|
||||
</div>
|
||||
<Button variant='ghost' size='icon' className='h-8 w-8' onClick={onClose}><X className='h-4 w-4' /></Button>
|
||||
</div>
|
||||
|
||||
<div className='mt-5 space-y-4 text-sm'>
|
||||
<Field label='运行状态'>
|
||||
<span className={cn('inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-semibold ring-1 ring-inset', st.cls)}>{st.text}</span>
|
||||
<span className='text-muted-foreground ml-2 text-[11px]'>{liveQ.isFetching ? '刷新中…' : '每 10 秒刷新'}</span>
|
||||
</Field>
|
||||
<Field label='访问地址(直连)'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='min-w-0 flex-1 truncate font-mono text-xs'>{agent.subdomain || '地址生成中…'}</span>
|
||||
<Button variant='outline' size='sm' className='h-7 gap-1 text-xs' disabled={!agent.subdomain} onClick={() => onCopy(agent.subdomain)}>
|
||||
<Copy className='h-3 w-3' /> 复制
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
<Field label='挂载的资源'>
|
||||
{(agent.binding_ids?.length ?? 0) === 0 ? (
|
||||
<span className='text-muted-foreground text-xs'>未挂载资源</span>
|
||||
) : (
|
||||
<div className='flex flex-wrap gap-1.5'>
|
||||
{agent.binding_ids.map((id) => {
|
||||
const Icon = RESOURCE_ICON[resourceType(id)] ?? Database
|
||||
return (
|
||||
<span key={id} className='inline-flex items-center gap-1 rounded-md border border-[color-mix(in_oklch,var(--primary)_18%,var(--border))] bg-[color-mix(in_oklch,var(--card)_60%,transparent)] px-2 py-0.5 text-[11px]'>
|
||||
<Icon className='h-3 w-3' /> {resourceName(id)}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
<Field label='运行时 ID'><span className='font-mono text-xs'>{agent.runtime_id || '—'}</span></Field>
|
||||
<Field label='部署时间'><span className='text-xs'>{agent.created_at}</span></Field>
|
||||
<Field label='更新时间'><span className='text-xs'>{agent.updated_at}</span></Field>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Field({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div>
|
||||
<p className='text-muted-foreground mb-1 text-[11px] font-semibold tracking-wider uppercase'>{label}</p>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Activity,
|
||||
Bot,
|
||||
Command,
|
||||
Download,
|
||||
@@ -57,6 +58,11 @@ export function useSidebarData(): SidebarData {
|
||||
url: '/deploy-agent',
|
||||
icon: Bot,
|
||||
},
|
||||
{
|
||||
title: t('Agent status'),
|
||||
url: '/agent-status',
|
||||
icon: Activity,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@
|
||||
"All statuses": "全部状态",
|
||||
"Agent runs": "运行总览",
|
||||
"Deploy sub agent": "部署 Sub Agent",
|
||||
"Agent status": "运行状态",
|
||||
"Resource binding": "资源绑定",
|
||||
"Bind your git repos / VMs / databases / blob. Credentials go straight to Azure Key Vault — only a secret_ref is stored.": "绑定你的 git 仓库 / 虚拟机 / 数据库 / 对象存储。凭据直接写入 Azure Key Vault,本地只保存 secret_ref。",
|
||||
"No resources bound yet": "还没有绑定任何资源",
|
||||
|
||||
+22
@@ -49,6 +49,7 @@ import { Route as AuthenticatedDeployAgentIndexRouteImport } from './routes/_aut
|
||||
import { Route as AuthenticatedDashboardIndexRouteImport } from './routes/_authenticated/dashboard/index'
|
||||
import { Route as AuthenticatedChannelsIndexRouteImport } from './routes/_authenticated/channels/index'
|
||||
import { Route as AuthenticatedAvailableModelsIndexRouteImport } from './routes/_authenticated/available-models/index'
|
||||
import { Route as AuthenticatedAgentStatusIndexRouteImport } from './routes/_authenticated/agent-status/index'
|
||||
import { Route as AuthenticatedUsageLogsSectionRouteImport } from './routes/_authenticated/usage-logs/$section'
|
||||
import { Route as AuthenticatedModelsSectionRouteImport } from './routes/_authenticated/models/$section'
|
||||
import { Route as AuthenticatedErrorsErrorRouteImport } from './routes/_authenticated/errors/$error'
|
||||
@@ -284,6 +285,12 @@ const AuthenticatedAvailableModelsIndexRoute =
|
||||
path: '/available-models/',
|
||||
getParentRoute: () => AuthenticatedRouteRoute,
|
||||
} as any)
|
||||
const AuthenticatedAgentStatusIndexRoute =
|
||||
AuthenticatedAgentStatusIndexRouteImport.update({
|
||||
id: '/agent-status/',
|
||||
path: '/agent-status/',
|
||||
getParentRoute: () => AuthenticatedRouteRoute,
|
||||
} as any)
|
||||
const AuthenticatedUsageLogsSectionRoute =
|
||||
AuthenticatedUsageLogsSectionRouteImport.update({
|
||||
id: '/usage-logs/$section',
|
||||
@@ -430,6 +437,7 @@ export interface FileRoutesByFullPath {
|
||||
'/errors/$error': typeof AuthenticatedErrorsErrorRoute
|
||||
'/models/$section': typeof AuthenticatedModelsSectionRoute
|
||||
'/usage-logs/$section': typeof AuthenticatedUsageLogsSectionRoute
|
||||
'/agent-status/': typeof AuthenticatedAgentStatusIndexRoute
|
||||
'/available-models/': typeof AuthenticatedAvailableModelsIndexRoute
|
||||
'/channels/': typeof AuthenticatedChannelsIndexRoute
|
||||
'/dashboard/': typeof AuthenticatedDashboardIndexRoute
|
||||
@@ -489,6 +497,7 @@ export interface FileRoutesByTo {
|
||||
'/errors/$error': typeof AuthenticatedErrorsErrorRoute
|
||||
'/models/$section': typeof AuthenticatedModelsSectionRoute
|
||||
'/usage-logs/$section': typeof AuthenticatedUsageLogsSectionRoute
|
||||
'/agent-status': typeof AuthenticatedAgentStatusIndexRoute
|
||||
'/available-models': typeof AuthenticatedAvailableModelsIndexRoute
|
||||
'/channels': typeof AuthenticatedChannelsIndexRoute
|
||||
'/dashboard': typeof AuthenticatedDashboardIndexRoute
|
||||
@@ -552,6 +561,7 @@ export interface FileRoutesById {
|
||||
'/_authenticated/errors/$error': typeof AuthenticatedErrorsErrorRoute
|
||||
'/_authenticated/models/$section': typeof AuthenticatedModelsSectionRoute
|
||||
'/_authenticated/usage-logs/$section': typeof AuthenticatedUsageLogsSectionRoute
|
||||
'/_authenticated/agent-status/': typeof AuthenticatedAgentStatusIndexRoute
|
||||
'/_authenticated/available-models/': typeof AuthenticatedAvailableModelsIndexRoute
|
||||
'/_authenticated/channels/': typeof AuthenticatedChannelsIndexRoute
|
||||
'/_authenticated/dashboard/': typeof AuthenticatedDashboardIndexRoute
|
||||
@@ -614,6 +624,7 @@ export interface FileRouteTypes {
|
||||
| '/errors/$error'
|
||||
| '/models/$section'
|
||||
| '/usage-logs/$section'
|
||||
| '/agent-status/'
|
||||
| '/available-models/'
|
||||
| '/channels/'
|
||||
| '/dashboard/'
|
||||
@@ -673,6 +684,7 @@ export interface FileRouteTypes {
|
||||
| '/errors/$error'
|
||||
| '/models/$section'
|
||||
| '/usage-logs/$section'
|
||||
| '/agent-status'
|
||||
| '/available-models'
|
||||
| '/channels'
|
||||
| '/dashboard'
|
||||
@@ -735,6 +747,7 @@ export interface FileRouteTypes {
|
||||
| '/_authenticated/errors/$error'
|
||||
| '/_authenticated/models/$section'
|
||||
| '/_authenticated/usage-logs/$section'
|
||||
| '/_authenticated/agent-status/'
|
||||
| '/_authenticated/available-models/'
|
||||
| '/_authenticated/channels/'
|
||||
| '/_authenticated/dashboard/'
|
||||
@@ -1069,6 +1082,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof AuthenticatedAvailableModelsIndexRouteImport
|
||||
parentRoute: typeof AuthenticatedRouteRoute
|
||||
}
|
||||
'/_authenticated/agent-status/': {
|
||||
id: '/_authenticated/agent-status/'
|
||||
path: '/agent-status'
|
||||
fullPath: '/agent-status/'
|
||||
preLoaderRoute: typeof AuthenticatedAgentStatusIndexRouteImport
|
||||
parentRoute: typeof AuthenticatedRouteRoute
|
||||
}
|
||||
'/_authenticated/usage-logs/$section': {
|
||||
id: '/_authenticated/usage-logs/$section'
|
||||
path: '/usage-logs/$section'
|
||||
@@ -1301,6 +1321,7 @@ interface AuthenticatedRouteRouteChildren {
|
||||
AuthenticatedErrorsErrorRoute: typeof AuthenticatedErrorsErrorRoute
|
||||
AuthenticatedModelsSectionRoute: typeof AuthenticatedModelsSectionRoute
|
||||
AuthenticatedUsageLogsSectionRoute: typeof AuthenticatedUsageLogsSectionRoute
|
||||
AuthenticatedAgentStatusIndexRoute: typeof AuthenticatedAgentStatusIndexRoute
|
||||
AuthenticatedAvailableModelsIndexRoute: typeof AuthenticatedAvailableModelsIndexRoute
|
||||
AuthenticatedChannelsIndexRoute: typeof AuthenticatedChannelsIndexRoute
|
||||
AuthenticatedDashboardIndexRoute: typeof AuthenticatedDashboardIndexRoute
|
||||
@@ -1328,6 +1349,7 @@ const AuthenticatedRouteRouteChildren: AuthenticatedRouteRouteChildren = {
|
||||
AuthenticatedErrorsErrorRoute: AuthenticatedErrorsErrorRoute,
|
||||
AuthenticatedModelsSectionRoute: AuthenticatedModelsSectionRoute,
|
||||
AuthenticatedUsageLogsSectionRoute: AuthenticatedUsageLogsSectionRoute,
|
||||
AuthenticatedAgentStatusIndexRoute: AuthenticatedAgentStatusIndexRoute,
|
||||
AuthenticatedAvailableModelsIndexRoute:
|
||||
AuthenticatedAvailableModelsIndexRoute,
|
||||
AuthenticatedChannelsIndexRoute: AuthenticatedChannelsIndexRoute,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { AgentStatusPage } from '@/features/agent-status/agent-status-page'
|
||||
|
||||
export const Route = createFileRoute('/_authenticated/agent-status/')({
|
||||
component: AgentStatusPage,
|
||||
})
|
||||
Reference in New Issue
Block a user