feat: Agent CLI 与终端审计回放全面优化

- Agent CLI: 多轮对话、SSH 长连接、流式推理、Evolver 按用户隔离、历史侧栏
- 终端审计: 回放播放器、命令书签、筛选分页导出、命令输出统计、录制落盘修复
- 修复 session_commands 表迁移、Kill 竞态导致录制丢失、无录制会话回放提示
- 审计历史页只读(移除删除),移除回放弹框录制搜索
This commit is contained in:
Logan committed 2026-07-01 12:13:53 +08:00
1 parent 9a3dd304f7
commit 1acb0c49af
81 files changed
+3916 -1655

No files matched your search

@@ -108,31 +108,31 @@ export default function NamespaceManagementPage() {
return (
<div className="space-y-4">
{/* Breadcrumb */}
<div className="flex items-center gap-2 text-sm text-slate-500">
<Link to="/kubernetes" className="hover:text-slate-300 transition-colors">集群</Link>
<div className="flex items-center gap-2 text-sm text-[var(--dg-text-muted)]">
<Link to="/kubernetes" className="hover:text-[var(--dg-text-secondary)] transition-colors">集群</Link>
<ChevronRight className="h-3 w-3" />
<Link to={`/kubernetes/${clusterCode}`} className="hover:text-slate-300 transition-colors">{clusterCode}</Link>
<Link to={`/kubernetes/${clusterCode}`} className="hover:text-[var(--dg-text-secondary)] transition-colors">{clusterCode}</Link>
<ChevronRight className="h-3 w-3" />
<span className="text-slate-300">命名空间管理</span>
<span className="text-[var(--dg-text-secondary)]">命名空间管理</span>
</div>
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-slate-100 flex items-center gap-2">
<Boxes className="h-6 w-6 text-emerald-400" />
<h1 className="text-2xl font-bold text-[var(--dg-text-primary)] flex items-center gap-2">
<Boxes className="h-6 w-6 text-[var(--dg-green)]" />
命名空间管理
</h1>
<p className="text-sm text-slate-500 mt-1">命名空间资源配额与生命周期管理</p>
<p className="text-sm text-[var(--dg-text-muted)] mt-1">命名空间资源配额与生命周期管理</p>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="icon" className="h-9 w-9 border-slate-700 text-slate-400"
<Button variant="outline" size="icon" className="h-9 w-9 border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
onClick={loadData} disabled={loading}>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
</Button>
<Dialog open={createOpen} onOpenChange={setCreateOpen}>
<DialogTrigger asChild>
<Button className="bg-blue-600 hover:bg-blue-700">
<Button className="bg-[var(--dg-blue)] hover:opacity-90">
<Plus className="h-4 w-4 mr-1" />创建命名空间
</Button>
</DialogTrigger>
@@ -149,7 +149,7 @@ export default function NamespaceManagementPage() {
placeholder="例如: my-app"
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
/>
<p className="text-xs text-slate-500">
<p className="text-xs text-[var(--dg-text-muted)]">
名称需符合 DNS-1123 标准(小写字母/数字/连字符)
</p>
</div>
@@ -168,34 +168,34 @@ export default function NamespaceManagementPage() {
{/* Stats cards */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-4 flex items-center justify-between">
<div><p className="text-xs text-slate-500">命名空间</p><p className="text-xl font-bold text-emerald-400">{namespaces.length}</p></div>
<Boxes className="h-6 w-6 opacity-30 text-emerald-400" />
<div><p className="text-xs text-[var(--dg-text-muted)]">命名空间</p><p className="text-xl font-bold text-[var(--dg-green)]">{namespaces.length}</p></div>
<Boxes className="h-6 w-6 opacity-30 text-[var(--dg-green)]" />
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-4 flex items-center justify-between">
<div><p className="text-xs text-slate-500">Deployments</p><p className="text-xl font-bold text-blue-400">{totalDeploys}</p></div>
<Container className="h-6 w-6 opacity-30 text-blue-400" />
<div><p className="text-xs text-[var(--dg-text-muted)]">Deployments</p><p className="text-xl font-bold text-[var(--dg-blue)]">{totalDeploys}</p></div>
<Container className="h-6 w-6 opacity-30 text-[var(--dg-blue)]" />
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-4 flex items-center justify-between">
<div><p className="text-xs text-slate-500">Pods</p><p className="text-xl font-bold text-purple-400">{totalPods}</p></div>
<Activity className="h-6 w-6 opacity-30 text-purple-400" />
<div><p className="text-xs text-[var(--dg-text-muted)]">Pods</p><p className="text-xl font-bold text-[var(--dg-purple)]">{totalPods}</p></div>
<Activity className="h-6 w-6 opacity-30 text-[var(--dg-purple)]" />
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-4 flex items-center justify-between">
<div><p className="text-xs text-slate-500">Services</p><p className="text-xl font-bold text-cyan-400">{totalSvcs}</p></div>
<Network className="h-6 w-6 opacity-30 text-cyan-400" />
<div><p className="text-xs text-[var(--dg-text-muted)]">Services</p><p className="text-xl font-bold text-[var(--dg-cyan)]">{totalSvcs}</p></div>
<Network className="h-6 w-6 opacity-30 text-[var(--dg-cyan)]" />
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-4 flex items-center justify-between">
<div><p className="text-xs text-slate-500">配额限制</p><p className="text-xl font-bold text-amber-400">{quotaCount}</p></div>
<Shield className="h-6 w-6 opacity-30 text-amber-400" />
<div><p className="text-xs text-[var(--dg-text-muted)]">配额限制</p><p className="text-xl font-bold text-[var(--dg-orange)]">{quotaCount}</p></div>
<Shield className="h-6 w-6 opacity-30 text-[var(--dg-orange)]" />
</CardContent>
</Card>
</div>
@@ -206,89 +206,89 @@ export default function NamespaceManagementPage() {
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="搜索命名空间..."
className="max-w-xs bg-slate-900 border-slate-700"
className="max-w-xs bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]"
/>
</div>
{/* Namespace table */}
<Card className="bg-slate-900 border-slate-800">
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow className="border-slate-800 hover:bg-transparent">
<TableHead className="text-slate-400">名称</TableHead>
<TableHead className="text-slate-400">状态</TableHead>
<TableHead className="text-slate-400">年龄</TableHead>
<TableHead className="text-slate-400 text-center">Deploy</TableHead>
<TableHead className="text-slate-400 text-center">Pods</TableHead>
<TableHead className="text-slate-400 text-center">Svc</TableHead>
<TableHead className="text-slate-400 text-center">配额</TableHead>
<TableHead className="text-slate-400">标签</TableHead>
<TableHead className="text-slate-400 text-center">操作</TableHead>
<TableRow className="border-[var(--dg-border)] hover:bg-transparent">
<TableHead className="text-[var(--dg-text-secondary)]">名称</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">状态</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">年龄</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-center">Deploy</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-center">Pods</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-center">Svc</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-center">配额</TableHead>
<TableHead className="text-[var(--dg-text-secondary)]">标签</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-center">操作</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{loading ? (
<TableRow><TableCell colSpan={9} className="text-center text-slate-500 py-12">
<TableRow><TableCell colSpan={9} className="text-center text-[var(--dg-text-muted)] py-12">
<Loader2 className="h-5 w-5 animate-spin mx-auto mb-2" />加载中...
</TableCell></TableRow>
) : filtered.length === 0 ? (
<TableRow><TableCell colSpan={9} className="text-center text-slate-500 py-12">
<TableRow><TableCell colSpan={9} className="text-center text-[var(--dg-text-muted)] py-12">
<Boxes className="h-8 w-8 mx-auto mb-2 opacity-50" />暂无命名空间
</TableCell></TableRow>
) : (
filtered.map((ns) => {
const isSystem = SYSTEM_NAMESPACES.has(ns.name);
return (
<TableRow key={ns.name} className="border-slate-800 hover:bg-slate-800/30">
<TableRow key={ns.name} className="border-[var(--dg-border)] hover:bg-[var(--dg-bg-tertiary)]/30">
<TableCell className="font-medium">
<button
className="text-blue-400 hover:text-blue-300 hover:underline text-left"
className="text-[var(--dg-blue)] hover:text-[var(--dg-blue)] hover:underline text-left"
onClick={() => navigate(`/kubernetes/${clusterCode}/namespace/${ns.name}`)}
>
{ns.name}
</button>
</TableCell>
<TableCell>
<Badge className={ns.status === 'Active' ? 'bg-emerald-500/20 text-emerald-400' : 'bg-red-500/20 text-red-400'}>
<Badge className={ns.status === 'Active' ? 'bg-[var(--dg-green)]/20 text-[var(--dg-green)]' : 'bg-[var(--dg-red)]/20 text-[var(--dg-red)]'}>
{ns.status === 'Active' ? <CheckCircle2 className="h-3 w-3 mr-1" /> : <AlertTriangle className="h-3 w-3 mr-1" />}
{ns.status}
</Badge>
</TableCell>
<TableCell className="text-sm text-slate-400">{ns.age}</TableCell>
<TableCell className="text-center text-sm text-slate-300">{ns.resource_counts?.deployments ?? 0}</TableCell>
<TableCell className="text-center text-sm text-slate-300">{ns.resource_counts?.pods ?? 0}</TableCell>
<TableCell className="text-center text-sm text-slate-300">{ns.resource_counts?.services ?? 0}</TableCell>
<TableCell className="text-sm text-[var(--dg-text-secondary)]">{ns.age}</TableCell>
<TableCell className="text-center text-sm text-[var(--dg-text-secondary)]">{ns.resource_counts?.deployments ?? 0}</TableCell>
<TableCell className="text-center text-sm text-[var(--dg-text-secondary)]">{ns.resource_counts?.pods ?? 0}</TableCell>
<TableCell className="text-center text-sm text-[var(--dg-text-secondary)]">{ns.resource_counts?.services ?? 0}</TableCell>
<TableCell className="text-center">
{ns.has_resource_quota ? (
<Badge className="bg-amber-500/20 text-amber-400" title={JSON.stringify(ns.resource_quotas?.[0]?.status)}>
<Badge className="bg-[var(--dg-orange)]/20 text-[var(--dg-orange)]" title={JSON.stringify(ns.resource_quotas?.[0]?.status)}>
<Shield className="h-3 w-3 mr-1" />有
</Badge>
) : (
<span className="text-xs text-slate-600">-</span>
<span className="text-xs text-[var(--dg-text-muted)]">-</span>
)}
</TableCell>
<TableCell className="text-xs">
{ns.labels && Object.keys(ns.labels).length > 0 ? (
<div className="flex flex-wrap gap-1 max-w-[200px]">
{Object.entries(ns.labels).slice(0, 3).map(([k, v]) => (
<span key={k} className="px-1.5 py-0.5 bg-slate-800 rounded text-slate-400">{k}={v}</span>
<span key={k} className="px-1.5 py-0.5 bg-[var(--dg-bg-tertiary)] rounded text-[var(--dg-text-secondary)]">{k}={v}</span>
))}
{Object.keys(ns.labels).length > 3 && (
<span className="text-slate-600">+{Object.keys(ns.labels).length - 3}</span>
<span className="text-[var(--dg-text-muted)]">+{Object.keys(ns.labels).length - 3}</span>
)}
</div>
) : (
<span className="text-slate-600">-</span>
<span className="text-[var(--dg-text-muted)]">-</span>
)}
</TableCell>
<TableCell className="text-center">
{isSystem ? (
<span className="text-xs text-slate-600">系统</span>
<span className="text-xs text-[var(--dg-text-muted)]">系统</span>
) : (
<Button
variant="ghost" size="icon"
className="h-7 w-7 text-red-400 hover:text-red-300 hover:bg-red-500/10"
className="h-7 w-7 text-[var(--dg-red)] hover:text-[var(--dg-red)] hover:bg-[var(--dg-red)]/10"
onClick={() => setDeleteTarget(ns.name)}
title="删除命名空间"
>
@@ -309,16 +309,16 @@ export default function NamespaceManagementPage() {
<Dialog open={!!deleteTarget} onOpenChange={(open) => !open && setDeleteTarget(null)}>
<DialogContent>
<DialogHeader>
<DialogTitle className="text-red-400 flex items-center gap-2">
<DialogTitle className="text-[var(--dg-red)] flex items-center gap-2">
<AlertTriangle className="h-5 w-5" />删除命名空间
</DialogTitle>
</DialogHeader>
<div className="space-y-3 py-4">
<div className="bg-red-500/10 border border-red-500/30 rounded-lg p-3">
<p className="text-sm text-red-300">
<div className="bg-[var(--dg-red)]/10 border border-[var(--dg-red)]/30 rounded-lg p-3">
<p className="text-sm text-[var(--dg-red)]">
⚠️ 即将删除命名空间 <span className="font-bold">{deleteTarget}</span> 及其中所有资源。
</p>
<p className="text-xs text-red-400/70 mt-1">
<p className="text-xs text-[var(--dg-red)]/70 mt-1">
此操作不可逆,所有 Deployment/Pod/Service/ConfigMap 等资源将被永久删除。
</p>
</div>