@winspan/claude-forge 8.33.2 → 8.33.5
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/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +32 -11
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/daemon/routing-observer.js +1 -1
- package/dist/daemon/routing-observer.js.map +1 -1
- package/dist/engine/conventions/routing.yaml +11 -0
- package/dist/web/routes/ai.d.ts.map +1 -1
- package/dist/web/routes/ai.js +2 -7
- package/dist/web/routes/ai.js.map +1 -1
- package/dist/web/routes/execution-trace.d.ts +8 -0
- package/dist/web/routes/execution-trace.d.ts.map +1 -1
- package/dist/web/routes/execution-trace.js +30 -1
- package/dist/web/routes/execution-trace.js.map +1 -1
- package/dist/web/routes/routing.d.ts.map +1 -1
- package/dist/web/routes/routing.js +11 -1
- package/dist/web/routes/routing.js.map +1 -1
- package/dist/web/static/assets/{AIConfig-Dw13rVtT.js → AIConfig-BbwRuWzm.js} +2 -2
- package/dist/web/static/assets/{AIConfig-Dw13rVtT.js.map → AIConfig-BbwRuWzm.js.map} +1 -1
- package/dist/web/static/assets/{Agents-D1p3JhQs.js → Agents-FyKzsN-g.js} +2 -2
- package/dist/web/static/assets/{Agents-D1p3JhQs.js.map → Agents-FyKzsN-g.js.map} +1 -1
- package/dist/web/static/assets/{Dashboard-qUCxXFSI.js → Dashboard-Ciyyw6ph.js} +2 -2
- package/dist/web/static/assets/{Dashboard-qUCxXFSI.js.map → Dashboard-Ciyyw6ph.js.map} +1 -1
- package/dist/web/static/assets/{Drawer-DeKukfwJ.js → Drawer-DOUcx6m1.js} +2 -2
- package/dist/web/static/assets/{Drawer-DeKukfwJ.js.map → Drawer-DOUcx6m1.js.map} +1 -1
- package/dist/web/static/assets/{Events-DqzBTrly.js → Events-C2OCOKAD.js} +2 -2
- package/dist/web/static/assets/{Events-DqzBTrly.js.map → Events-C2OCOKAD.js.map} +1 -1
- package/dist/web/static/assets/ExecutionTrace-BsWSpu2v.js +3 -0
- package/dist/web/static/assets/ExecutionTrace-BsWSpu2v.js.map +1 -0
- package/dist/web/static/assets/{Methodologies-Br5KOWuF.js → Methodologies-DwYLXc8o.js} +2 -2
- package/dist/web/static/assets/{Methodologies-Br5KOWuF.js.map → Methodologies-DwYLXc8o.js.map} +1 -1
- package/dist/web/static/assets/{MethodologyDetail-Do1taSKM.js → MethodologyDetail-rV3W1utf.js} +2 -2
- package/dist/web/static/assets/{MethodologyDetail-Do1taSKM.js.map → MethodologyDetail-rV3W1utf.js.map} +1 -1
- package/dist/web/static/assets/{Routing-CFmM7JuB.js → Routing-BW3eGD-8.js} +2 -2
- package/dist/web/static/assets/{Routing-CFmM7JuB.js.map → Routing-BW3eGD-8.js.map} +1 -1
- package/dist/web/static/assets/{SessionDetail-DzTue2xK.js → SessionDetail-Cbd7Jwox.js} +2 -2
- package/dist/web/static/assets/{SessionDetail-DzTue2xK.js.map → SessionDetail-Cbd7Jwox.js.map} +1 -1
- package/dist/web/static/assets/{Sessions-DOd65EkN.js → Sessions-CFpFk-5I.js} +2 -2
- package/dist/web/static/assets/{Sessions-DOd65EkN.js.map → Sessions-CFpFk-5I.js.map} +1 -1
- package/dist/web/static/assets/{Skills-zacj_uSW.js → Skills-BkbegTV1.js} +2 -2
- package/dist/web/static/assets/{Skills-zacj_uSW.js.map → Skills-BkbegTV1.js.map} +1 -1
- package/dist/web/static/assets/{export-CEzDNM66.js → export-CbQTOt71.js} +2 -2
- package/dist/web/static/assets/{export-CEzDNM66.js.map → export-CbQTOt71.js.map} +1 -1
- package/dist/web/static/assets/{index-BVpUTHdp.js → index-CIRlno4E.js} +3 -3
- package/dist/web/static/assets/{index-BVpUTHdp.js.map → index-CIRlno4E.js.map} +1 -1
- package/dist/web/static/assets/index-N1EByy_h.css +1 -0
- package/dist/web/static/assets/{lucide-DjB4fWNj.js → lucide-BanPULT1.js} +35 -40
- package/dist/web/static/assets/lucide-BanPULT1.js.map +1 -0
- package/dist/web/static/index.html +3 -3
- package/package.json +1 -1
- package/dist/web/static/assets/ExecutionTrace-Z-zlH0KH.js +0 -2
- package/dist/web/static/assets/ExecutionTrace-Z-zlH0KH.js.map +0 -1
- package/dist/web/static/assets/index-Drpf7sLl.css +0 -1
- package/dist/web/static/assets/lucide-DjB4fWNj.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MethodologyDetail-Do1taSKM.js","sources":["../../src/pages/MethodologyDetail.tsx"],"sourcesContent":["import { useParams, Link } from 'react-router-dom'\nimport { useQuery } from '@tanstack/react-query'\nimport { useEffect, useRef, useState } from 'react'\nimport { formatDistanceToNow, format } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport {\n ArrowLeft,\n CheckCircle,\n Clock,\n Circle,\n Loader2,\n User,\n FileText,\n AlertCircle,\n Activity,\n Cpu,\n Monitor,\n} from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface PlannedPhase {\n id: string\n agent: string\n prompt: string\n rationale: string\n artifacts?: Array<{ type: string; pattern?: string; required?: boolean; min?: number }>\n}\n\ninterface ExecutionPlan {\n phases: PlannedPhase[]\n rationale: string\n methodology_id: string\n}\n\ninterface PhaseExecution {\n id: number\n methodology_execution_id: number\n phase_id: string\n phase_index: number\n agent_name: string\n prompt: string\n status: 'pending' | 'running' | 'completed' | 'failed'\n started_at: number | null\n completed_at: number | null\n duration_ms: number | null\n artifacts_json: string | null\n output_text?: string | null\n error_message?: string | null\n stream_log_path?: string | null\n}\n\ninterface MethodologyDetail {\n id: number\n session_id: string\n methodology_id: string\n plan_json: string\n plan: ExecutionPlan\n current_phase_index: number\n status: 'running' | 'completed' | 'failed' | 'cancelled'\n mode?: 'foreground' | 'background'\n worker_pid?: number | null\n worker_session_id?: string | null\n started_at: number\n completed_at: number | null\n phases: PhaseExecution[]\n}\n\nasync function fetchMethodologyDetail(id: string): Promise<MethodologyDetail> {\n const res = await fetch(`/api/methodology-executions/${id}`)\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nconst methodologyNames: Record<string, string> = {\n 'harness-engineering': 'Harness Engineering',\n 'bmad': 'BMad (敏捷开发)',\n}\n\nexport default function MethodologyDetail() {\n const { id } = useParams<{ id: string }>()\n const { data, isLoading, error } = useQuery({\n queryKey: ['methodology-execution', id],\n queryFn: () => fetchMethodologyDetail(id!),\n enabled: !!id,\n refetchInterval: (query) => {\n const data = query.state.data\n return data?.status === 'running' ? 3000 : false\n },\n })\n\n const liveEvents = useExecutorSSE(id, data?.status === 'running')\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-indigo-600\" />\n </div>\n )\n }\n\n if (error || !data) {\n return (\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error)?.message || '未知错误'}</p>\n <Link to=\"/methodologies\" className=\"mt-4 inline-flex items-center text-indigo-600 hover:text-indigo-700\">\n <ArrowLeft className=\"h-4 w-4 mr-1\" />\n 返回列表\n </Link>\n </div>\n )\n }\n\n const { plan, phases, current_phase_index, status } = data\n const progress = status === 'completed' ? 100 : Math.round((current_phase_index / plan.phases.length) * 100)\n\n return (\n <div>\n {/* Header */}\n <div className=\"mb-6\">\n <Link\n to=\"/methodologies\"\n className=\"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4\"\n >\n <ArrowLeft className=\"h-4 w-4 mr-1\" />\n 返回列表\n </Link>\n <div className=\"flex items-start justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900\">\n {methodologyNames[data.methodology_id] || data.methodology_id}\n </h1>\n <p className=\"text-sm text-gray-500 mt-1\">\n Session: {data.session_id}\n </p>\n </div>\n <span className={clsx(\n 'px-3 py-1 text-sm font-medium rounded-full',\n status === 'running' && 'bg-blue-100 text-blue-700',\n status === 'completed' && 'bg-green-100 text-green-700',\n status === 'failed' && 'bg-red-100 text-red-700'\n )}>\n {status === 'running' && '运行中'}\n {status === 'completed' && '已完成'}\n {status === 'failed' && '失败'}\n </span>\n </div>\n </div>\n\n {/* Progress */}\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-medium text-gray-700\">执行进度</h3>\n <div className=\"flex items-center gap-2 text-sm text-gray-500\">\n {data.mode && (\n <span\n className={clsx(\n 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs',\n data.mode === 'background' ? 'bg-purple-100 text-purple-700' : 'bg-sky-100 text-sky-700',\n )}\n >\n {data.mode === 'background' ? <Cpu className=\"h-3 w-3\" /> : <Monitor className=\"h-3 w-3\" />}\n {data.mode === 'background' ? '后台' : '前台'}\n </span>\n )}\n <span>{progress}%</span>\n </div>\n </div>\n <div className=\"w-full bg-gray-200 rounded-full h-2\">\n <div\n className={clsx(\n 'h-2 rounded-full transition-all duration-500',\n status === 'completed' ? 'bg-green-500' : 'bg-indigo-600'\n )}\n style={{ width: `${progress}%` }}\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4 mt-4 pt-4 border-t\">\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm font-medium\">\n {format(data.started_at, 'MM-dd HH:mm:ss')}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">阶段</div>\n <div className=\"text-sm font-medium\">\n {current_phase_index + (status === 'completed' ? 0 : 1)} / {plan.phases.length}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">\n {status === 'completed' ? '完成时间' : '已运行'}\n </div>\n <div className=\"text-sm font-medium\">\n {status === 'completed' && data.completed_at\n ? format(data.completed_at, 'MM-dd HH:mm:ss')\n : formatDistanceToNow(data.started_at, { addSuffix: false, locale: zhCN })}\n </div>\n </div>\n </div>\n {(data.worker_pid || data.worker_session_id) && (\n <div className=\"mt-3 pt-3 border-t text-xs text-gray-500 flex gap-4\">\n {data.worker_pid ? <span>worker PID: <span className=\"font-mono\">{data.worker_pid}</span></span> : null}\n {data.worker_session_id ? (\n <span>worker session: <span className=\"font-mono\">{String(data.worker_session_id).slice(0, 18)}…</span></span>\n ) : null}\n </div>\n )}\n </div>\n\n {/* Live executor events (SSE) */}\n {(status === 'running' || liveEvents.length > 0) && (\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <h3 className=\"flex items-center text-sm font-medium text-gray-700 mb-3\">\n <Activity className=\"h-4 w-4 mr-2 text-indigo-600\" />\n 实时事件流\n <span className=\"ml-2 text-xs text-gray-400\">(最近 {liveEvents.length} 条)</span>\n </h3>\n {liveEvents.length === 0 ? (\n <p className=\"text-xs text-gray-500\">\n {status === 'running' ? '等待 worker 输出…' : '暂无事件'}\n </p>\n ) : (\n <ol className=\"space-y-1 text-xs font-mono max-h-64 overflow-auto\">\n {liveEvents.map((ev, i) => (\n <li key={i} className=\"flex items-start gap-2\">\n <span className=\"text-gray-400 flex-shrink-0 w-16\">\n {format(ev.timestamp, 'HH:mm:ss')}\n </span>\n <span className={clsx(\n 'px-1.5 rounded text-[10px] font-medium flex-shrink-0',\n ev.type === 'phase-start' && 'bg-indigo-100 text-indigo-700',\n ev.type === 'phase-finish' && 'bg-green-100 text-green-700',\n ev.type === 'phase-log' && 'bg-gray-100 text-gray-700',\n ev.type === 'error' && 'bg-red-100 text-red-700',\n ev.type === 'done' && 'bg-gray-200 text-gray-700',\n )}>\n {ev.type}\n </span>\n <span className=\"text-gray-700 truncate\">\n {ev.phase_id ? `phase=${ev.phase_id} ` : ''}\n {ev.payload ? JSON.stringify(ev.payload).slice(0, 180) : ''}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n )}\n\n {/* Plan Rationale */}\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <h3 className=\"flex items-center text-sm font-medium text-gray-700 mb-3\">\n <AlertCircle className=\"h-4 w-4 mr-2 text-indigo-600\" />\n 执行计划 (AI 生成)\n </h3>\n <p className=\"text-sm text-gray-600 leading-relaxed\">\n {plan.rationale}\n </p>\n </div>\n\n {/* Phase Timeline */}\n <div className=\"bg-white rounded-lg shadow p-6\">\n <h3 className=\"text-lg font-semibold text-gray-900 mb-4\">执行阶段</h3>\n\n <div className=\"space-y-4\">\n {plan.phases.map((phase, index) => {\n const phaseExec = phases.find((p) => p.phase_index === index)\n const isCompleted = index < current_phase_index ||\n (index === current_phase_index && status === 'completed') ||\n phaseExec?.status === 'completed'\n const isRunning = index === current_phase_index && status === 'running' && phaseExec?.status === 'running'\n const isPending = index > current_phase_index && !isRunning\n\n return (\n <div key={index} className=\"relative\">\n {/* Timeline line */}\n {index < plan.phases.length - 1 && (\n <div\n className={clsx(\n 'absolute left-4 top-10 w-0.5 h-full -z-10',\n isCompleted ? 'bg-green-300' : 'bg-gray-200'\n )}\n />\n )}\n\n <div className=\"flex gap-4\">\n {/* Status icon */}\n <div className=\"flex-shrink-0\">\n {isCompleted ? (\n <div className=\"w-8 h-8 rounded-full bg-green-100 flex items-center justify-center\">\n <CheckCircle className=\"h-5 w-5 text-green-600\" />\n </div>\n ) : isRunning ? (\n <div className=\"w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center\">\n <Loader2 className=\"h-5 w-5 text-blue-600 animate-spin\" />\n </div>\n ) : (\n <div className=\"w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center\">\n <Circle className=\"h-5 w-5 text-gray-400\" />\n </div>\n )}\n </div>\n\n {/* Phase content */}\n <div className={clsx(\n 'flex-1 pb-4',\n isPending && 'opacity-50'\n )}>\n <div className=\"bg-gray-50 rounded-lg p-4 border border-gray-200\">\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm font-semibold text-gray-900\">\n 阶段 {index + 1}: {phase.id}\n </span>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded',\n isCompleted && 'bg-green-100 text-green-700',\n isRunning && 'bg-blue-100 text-blue-700',\n isPending && 'bg-gray-100 text-gray-600'\n )}>\n {isCompleted && '已完成'}\n {isRunning && '运行中'}\n {isPending && '待执行'}\n </span>\n </div>\n {phaseExec?.duration_ms && (\n <span className=\"text-xs text-gray-500\">\n <Clock className=\"inline h-3 w-3 mr-1\" />\n {(phaseExec.duration_ms / 1000).toFixed(1)}s\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 mb-3 text-xs text-gray-600\">\n <User className=\"h-3 w-3\" />\n <span>Agent: <strong>{phase.agent}</strong></span>\n </div>\n\n <p className=\"text-xs text-gray-500 italic mb-3\">\n {phase.rationale}\n </p>\n\n <div className=\"bg-white rounded p-3 border border-gray-200\">\n <div className=\"flex items-center gap-1 text-xs text-gray-500 mb-1\">\n <FileText className=\"h-3 w-3\" />\n Prompt\n </div>\n <p className=\"text-xs text-gray-700 leading-relaxed\">\n {phase.prompt}\n </p>\n </div>\n\n {phase.artifacts && phase.artifacts.length > 0 && (\n <div className=\"mt-3\">\n <div className=\"text-xs text-gray-500 mb-1\">预期产物</div>\n <div className=\"flex flex-wrap gap-1\">\n {phase.artifacts.map((artifact, i) => (\n <span\n key={i}\n className=\"px-2 py-0.5 text-xs bg-indigo-50 text-indigo-700 rounded\"\n >\n {artifact.pattern || artifact.type}\n {artifact.required && ' *'}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {phaseExec?.output_text && (\n <details className=\"mt-3 bg-white rounded border border-gray-200\">\n <summary className=\"px-3 py-1.5 cursor-pointer text-xs text-gray-600 hover:bg-gray-50\">\n <FileText className=\"inline h-3 w-3 mr-1\" /> 输出({phaseExec.output_text.length} 字符)\n </summary>\n <pre className=\"px-3 py-2 text-xs text-gray-800 whitespace-pre-wrap break-words max-h-64 overflow-auto\">\n{phaseExec.output_text}\n </pre>\n </details>\n )}\n\n {phaseExec?.error_message && (\n <div className=\"mt-3 bg-red-50 border border-red-200 rounded p-2\">\n <div className=\"flex items-center gap-1 text-xs font-medium text-red-700 mb-1\">\n <AlertCircle className=\"h-3 w-3\" /> 错误\n </div>\n <pre className=\"text-xs text-red-800 whitespace-pre-wrap break-words\">\n{phaseExec.error_message}\n </pre>\n </div>\n )}\n\n {phaseExec?.stream_log_path && (\n <div className=\"mt-2 text-[11px] text-gray-400\">\n 日志: <span className=\"font-mono\">{phaseExec.stream_log_path}</span>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n\ninterface ExecutorEventPayload {\n type: 'phase-start' | 'phase-log' | 'phase-finish' | 'done' | 'error'\n execution_id: number\n phase_index?: number\n phase_id?: string\n payload?: unknown\n timestamp: number\n}\n\nfunction useExecutorSSE(executionId: string | undefined, enabled: boolean) {\n const [events, setEvents] = useState<ExecutorEventPayload[]>([])\n const esRef = useRef<EventSource | null>(null)\n\n useEffect(() => {\n if (!executionId || !enabled) {\n esRef.current?.close()\n esRef.current = null\n return\n }\n const es = new EventSource(`/api/methodology-executions/events?execution_id=${executionId}`)\n esRef.current = es\n const handler = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data) as ExecutorEventPayload\n setEvents(prev => {\n const next = [...prev, data]\n if (next.length > 100) next.splice(0, next.length - 100)\n return next\n })\n } catch { /* ignore malformed */ }\n }\n ;(['phase-start', 'phase-log', 'phase-finish', 'done', 'error'] as const).forEach(t => {\n es.addEventListener(t, handler as EventListener)\n })\n return () => {\n es.close()\n esRef.current = null\n }\n }, [executionId, enabled])\n\n return events\n}\n"],"names":["fetchMethodologyDetail","id","res","methodologyNames","MethodologyDetail","useParams","data","isLoading","error","useQuery","query","liveEvents","useExecutorSSE","jsx","Loader2","jsxs","Link","ArrowLeft","plan","phases","current_phase_index","status","progress","clsx","Cpu","Monitor","format","formatDistanceToNow","zhCN","Activity","ev","i","AlertCircle","phase","index","phaseExec","p","isCompleted","isRunning","isPending","CheckCircle","Circle","Clock","User","FileText","artifact","executionId","enabled","events","setEvents","useState","esRef","useRef","useEffect","_a","es","handler","e","prev","next","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MethodologyDetail-rV3W1utf.js","sources":["../../src/pages/MethodologyDetail.tsx"],"sourcesContent":["import { useParams, Link } from 'react-router-dom'\nimport { useQuery } from '@tanstack/react-query'\nimport { useEffect, useRef, useState } from 'react'\nimport { formatDistanceToNow, format } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport {\n ArrowLeft,\n CheckCircle,\n Clock,\n Circle,\n Loader2,\n User,\n FileText,\n AlertCircle,\n Activity,\n Cpu,\n Monitor,\n} from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface PlannedPhase {\n id: string\n agent: string\n prompt: string\n rationale: string\n artifacts?: Array<{ type: string; pattern?: string; required?: boolean; min?: number }>\n}\n\ninterface ExecutionPlan {\n phases: PlannedPhase[]\n rationale: string\n methodology_id: string\n}\n\ninterface PhaseExecution {\n id: number\n methodology_execution_id: number\n phase_id: string\n phase_index: number\n agent_name: string\n prompt: string\n status: 'pending' | 'running' | 'completed' | 'failed'\n started_at: number | null\n completed_at: number | null\n duration_ms: number | null\n artifacts_json: string | null\n output_text?: string | null\n error_message?: string | null\n stream_log_path?: string | null\n}\n\ninterface MethodologyDetail {\n id: number\n session_id: string\n methodology_id: string\n plan_json: string\n plan: ExecutionPlan\n current_phase_index: number\n status: 'running' | 'completed' | 'failed' | 'cancelled'\n mode?: 'foreground' | 'background'\n worker_pid?: number | null\n worker_session_id?: string | null\n started_at: number\n completed_at: number | null\n phases: PhaseExecution[]\n}\n\nasync function fetchMethodologyDetail(id: string): Promise<MethodologyDetail> {\n const res = await fetch(`/api/methodology-executions/${id}`)\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nconst methodologyNames: Record<string, string> = {\n 'harness-engineering': 'Harness Engineering',\n 'bmad': 'BMad (敏捷开发)',\n}\n\nexport default function MethodologyDetail() {\n const { id } = useParams<{ id: string }>()\n const { data, isLoading, error } = useQuery({\n queryKey: ['methodology-execution', id],\n queryFn: () => fetchMethodologyDetail(id!),\n enabled: !!id,\n refetchInterval: (query) => {\n const data = query.state.data\n return data?.status === 'running' ? 3000 : false\n },\n })\n\n const liveEvents = useExecutorSSE(id, data?.status === 'running')\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-indigo-600\" />\n </div>\n )\n }\n\n if (error || !data) {\n return (\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error)?.message || '未知错误'}</p>\n <Link to=\"/methodologies\" className=\"mt-4 inline-flex items-center text-indigo-600 hover:text-indigo-700\">\n <ArrowLeft className=\"h-4 w-4 mr-1\" />\n 返回列表\n </Link>\n </div>\n )\n }\n\n const { plan, phases, current_phase_index, status } = data\n const progress = status === 'completed' ? 100 : Math.round((current_phase_index / plan.phases.length) * 100)\n\n return (\n <div>\n {/* Header */}\n <div className=\"mb-6\">\n <Link\n to=\"/methodologies\"\n className=\"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4\"\n >\n <ArrowLeft className=\"h-4 w-4 mr-1\" />\n 返回列表\n </Link>\n <div className=\"flex items-start justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900\">\n {methodologyNames[data.methodology_id] || data.methodology_id}\n </h1>\n <p className=\"text-sm text-gray-500 mt-1\">\n Session: {data.session_id}\n </p>\n </div>\n <span className={clsx(\n 'px-3 py-1 text-sm font-medium rounded-full',\n status === 'running' && 'bg-blue-100 text-blue-700',\n status === 'completed' && 'bg-green-100 text-green-700',\n status === 'failed' && 'bg-red-100 text-red-700'\n )}>\n {status === 'running' && '运行中'}\n {status === 'completed' && '已完成'}\n {status === 'failed' && '失败'}\n </span>\n </div>\n </div>\n\n {/* Progress */}\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-medium text-gray-700\">执行进度</h3>\n <div className=\"flex items-center gap-2 text-sm text-gray-500\">\n {data.mode && (\n <span\n className={clsx(\n 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs',\n data.mode === 'background' ? 'bg-purple-100 text-purple-700' : 'bg-sky-100 text-sky-700',\n )}\n >\n {data.mode === 'background' ? <Cpu className=\"h-3 w-3\" /> : <Monitor className=\"h-3 w-3\" />}\n {data.mode === 'background' ? '后台' : '前台'}\n </span>\n )}\n <span>{progress}%</span>\n </div>\n </div>\n <div className=\"w-full bg-gray-200 rounded-full h-2\">\n <div\n className={clsx(\n 'h-2 rounded-full transition-all duration-500',\n status === 'completed' ? 'bg-green-500' : 'bg-indigo-600'\n )}\n style={{ width: `${progress}%` }}\n />\n </div>\n <div className=\"grid grid-cols-3 gap-4 mt-4 pt-4 border-t\">\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm font-medium\">\n {format(data.started_at, 'MM-dd HH:mm:ss')}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">阶段</div>\n <div className=\"text-sm font-medium\">\n {current_phase_index + (status === 'completed' ? 0 : 1)} / {plan.phases.length}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">\n {status === 'completed' ? '完成时间' : '已运行'}\n </div>\n <div className=\"text-sm font-medium\">\n {status === 'completed' && data.completed_at\n ? format(data.completed_at, 'MM-dd HH:mm:ss')\n : formatDistanceToNow(data.started_at, { addSuffix: false, locale: zhCN })}\n </div>\n </div>\n </div>\n {(data.worker_pid || data.worker_session_id) && (\n <div className=\"mt-3 pt-3 border-t text-xs text-gray-500 flex gap-4\">\n {data.worker_pid ? <span>worker PID: <span className=\"font-mono\">{data.worker_pid}</span></span> : null}\n {data.worker_session_id ? (\n <span>worker session: <span className=\"font-mono\">{String(data.worker_session_id).slice(0, 18)}…</span></span>\n ) : null}\n </div>\n )}\n </div>\n\n {/* Live executor events (SSE) */}\n {(status === 'running' || liveEvents.length > 0) && (\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <h3 className=\"flex items-center text-sm font-medium text-gray-700 mb-3\">\n <Activity className=\"h-4 w-4 mr-2 text-indigo-600\" />\n 实时事件流\n <span className=\"ml-2 text-xs text-gray-400\">(最近 {liveEvents.length} 条)</span>\n </h3>\n {liveEvents.length === 0 ? (\n <p className=\"text-xs text-gray-500\">\n {status === 'running' ? '等待 worker 输出…' : '暂无事件'}\n </p>\n ) : (\n <ol className=\"space-y-1 text-xs font-mono max-h-64 overflow-auto\">\n {liveEvents.map((ev, i) => (\n <li key={i} className=\"flex items-start gap-2\">\n <span className=\"text-gray-400 flex-shrink-0 w-16\">\n {format(ev.timestamp, 'HH:mm:ss')}\n </span>\n <span className={clsx(\n 'px-1.5 rounded text-[10px] font-medium flex-shrink-0',\n ev.type === 'phase-start' && 'bg-indigo-100 text-indigo-700',\n ev.type === 'phase-finish' && 'bg-green-100 text-green-700',\n ev.type === 'phase-log' && 'bg-gray-100 text-gray-700',\n ev.type === 'error' && 'bg-red-100 text-red-700',\n ev.type === 'done' && 'bg-gray-200 text-gray-700',\n )}>\n {ev.type}\n </span>\n <span className=\"text-gray-700 truncate\">\n {ev.phase_id ? `phase=${ev.phase_id} ` : ''}\n {ev.payload ? JSON.stringify(ev.payload).slice(0, 180) : ''}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n )}\n\n {/* Plan Rationale */}\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <h3 className=\"flex items-center text-sm font-medium text-gray-700 mb-3\">\n <AlertCircle className=\"h-4 w-4 mr-2 text-indigo-600\" />\n 执行计划 (AI 生成)\n </h3>\n <p className=\"text-sm text-gray-600 leading-relaxed\">\n {plan.rationale}\n </p>\n </div>\n\n {/* Phase Timeline */}\n <div className=\"bg-white rounded-lg shadow p-6\">\n <h3 className=\"text-lg font-semibold text-gray-900 mb-4\">执行阶段</h3>\n\n <div className=\"space-y-4\">\n {plan.phases.map((phase, index) => {\n const phaseExec = phases.find((p) => p.phase_index === index)\n const isCompleted = index < current_phase_index ||\n (index === current_phase_index && status === 'completed') ||\n phaseExec?.status === 'completed'\n const isRunning = index === current_phase_index && status === 'running' && phaseExec?.status === 'running'\n const isPending = index > current_phase_index && !isRunning\n\n return (\n <div key={index} className=\"relative\">\n {/* Timeline line */}\n {index < plan.phases.length - 1 && (\n <div\n className={clsx(\n 'absolute left-4 top-10 w-0.5 h-full -z-10',\n isCompleted ? 'bg-green-300' : 'bg-gray-200'\n )}\n />\n )}\n\n <div className=\"flex gap-4\">\n {/* Status icon */}\n <div className=\"flex-shrink-0\">\n {isCompleted ? (\n <div className=\"w-8 h-8 rounded-full bg-green-100 flex items-center justify-center\">\n <CheckCircle className=\"h-5 w-5 text-green-600\" />\n </div>\n ) : isRunning ? (\n <div className=\"w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center\">\n <Loader2 className=\"h-5 w-5 text-blue-600 animate-spin\" />\n </div>\n ) : (\n <div className=\"w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center\">\n <Circle className=\"h-5 w-5 text-gray-400\" />\n </div>\n )}\n </div>\n\n {/* Phase content */}\n <div className={clsx(\n 'flex-1 pb-4',\n isPending && 'opacity-50'\n )}>\n <div className=\"bg-gray-50 rounded-lg p-4 border border-gray-200\">\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm font-semibold text-gray-900\">\n 阶段 {index + 1}: {phase.id}\n </span>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded',\n isCompleted && 'bg-green-100 text-green-700',\n isRunning && 'bg-blue-100 text-blue-700',\n isPending && 'bg-gray-100 text-gray-600'\n )}>\n {isCompleted && '已完成'}\n {isRunning && '运行中'}\n {isPending && '待执行'}\n </span>\n </div>\n {phaseExec?.duration_ms && (\n <span className=\"text-xs text-gray-500\">\n <Clock className=\"inline h-3 w-3 mr-1\" />\n {(phaseExec.duration_ms / 1000).toFixed(1)}s\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 mb-3 text-xs text-gray-600\">\n <User className=\"h-3 w-3\" />\n <span>Agent: <strong>{phase.agent}</strong></span>\n </div>\n\n <p className=\"text-xs text-gray-500 italic mb-3\">\n {phase.rationale}\n </p>\n\n <div className=\"bg-white rounded p-3 border border-gray-200\">\n <div className=\"flex items-center gap-1 text-xs text-gray-500 mb-1\">\n <FileText className=\"h-3 w-3\" />\n Prompt\n </div>\n <p className=\"text-xs text-gray-700 leading-relaxed\">\n {phase.prompt}\n </p>\n </div>\n\n {phase.artifacts && phase.artifacts.length > 0 && (\n <div className=\"mt-3\">\n <div className=\"text-xs text-gray-500 mb-1\">预期产物</div>\n <div className=\"flex flex-wrap gap-1\">\n {phase.artifacts.map((artifact, i) => (\n <span\n key={i}\n className=\"px-2 py-0.5 text-xs bg-indigo-50 text-indigo-700 rounded\"\n >\n {artifact.pattern || artifact.type}\n {artifact.required && ' *'}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {phaseExec?.output_text && (\n <details className=\"mt-3 bg-white rounded border border-gray-200\">\n <summary className=\"px-3 py-1.5 cursor-pointer text-xs text-gray-600 hover:bg-gray-50\">\n <FileText className=\"inline h-3 w-3 mr-1\" /> 输出({phaseExec.output_text.length} 字符)\n </summary>\n <pre className=\"px-3 py-2 text-xs text-gray-800 whitespace-pre-wrap break-words max-h-64 overflow-auto\">\n{phaseExec.output_text}\n </pre>\n </details>\n )}\n\n {phaseExec?.error_message && (\n <div className=\"mt-3 bg-red-50 border border-red-200 rounded p-2\">\n <div className=\"flex items-center gap-1 text-xs font-medium text-red-700 mb-1\">\n <AlertCircle className=\"h-3 w-3\" /> 错误\n </div>\n <pre className=\"text-xs text-red-800 whitespace-pre-wrap break-words\">\n{phaseExec.error_message}\n </pre>\n </div>\n )}\n\n {phaseExec?.stream_log_path && (\n <div className=\"mt-2 text-[11px] text-gray-400\">\n 日志: <span className=\"font-mono\">{phaseExec.stream_log_path}</span>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n\ninterface ExecutorEventPayload {\n type: 'phase-start' | 'phase-log' | 'phase-finish' | 'done' | 'error'\n execution_id: number\n phase_index?: number\n phase_id?: string\n payload?: unknown\n timestamp: number\n}\n\nfunction useExecutorSSE(executionId: string | undefined, enabled: boolean) {\n const [events, setEvents] = useState<ExecutorEventPayload[]>([])\n const esRef = useRef<EventSource | null>(null)\n\n useEffect(() => {\n if (!executionId || !enabled) {\n esRef.current?.close()\n esRef.current = null\n return\n }\n const es = new EventSource(`/api/methodology-executions/events?execution_id=${executionId}`)\n esRef.current = es\n const handler = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data) as ExecutorEventPayload\n setEvents(prev => {\n const next = [...prev, data]\n if (next.length > 100) next.splice(0, next.length - 100)\n return next\n })\n } catch { /* ignore malformed */ }\n }\n ;(['phase-start', 'phase-log', 'phase-finish', 'done', 'error'] as const).forEach(t => {\n es.addEventListener(t, handler as EventListener)\n })\n return () => {\n es.close()\n esRef.current = null\n }\n }, [executionId, enabled])\n\n return events\n}\n"],"names":["fetchMethodologyDetail","id","res","methodologyNames","MethodologyDetail","useParams","data","isLoading","error","useQuery","query","liveEvents","useExecutorSSE","jsx","Loader2","jsxs","Link","ArrowLeft","plan","phases","current_phase_index","status","progress","clsx","Cpu","Monitor","format","formatDistanceToNow","zhCN","Activity","ev","i","AlertCircle","phase","index","phaseExec","p","isCompleted","isRunning","isPending","CheckCircle","Circle","Clock","User","FileText","artifact","executionId","enabled","events","setEvents","useState","esRef","useRef","useEffect","_a","es","handler","e","prev","next","t"],"mappings":"qWAmEA,eAAeA,EAAuBC,EAAwC,CAC5E,MAAMC,EAAM,MAAM,MAAM,+BAA+BD,CAAE,EAAE,EAC3D,GAAI,CAACC,EAAI,GAAI,MAAM,IAAI,MAAM,iBAAiB,EAC9C,OAAOA,EAAI,KAAA,CACb,CAEA,MAAMC,EAA2C,CAC/C,sBAAuB,sBACvB,KAAQ,aACV,EAEA,SAAwBC,GAAoB,CAC1C,KAAM,CAAE,GAAAH,CAAA,EAAOI,EAAA,EACT,CAAE,KAAAC,EAAM,UAAAC,EAAW,MAAAC,CAAA,EAAUC,EAAS,CAC1C,SAAU,CAAC,wBAAyBR,CAAE,EACtC,QAAS,IAAMD,EAAuBC,CAAG,EACzC,QAAS,CAAC,CAACA,EACX,gBAAkBS,GAAU,CAC1B,MAAMJ,EAAOI,EAAM,MAAM,KACzB,OAAOJ,GAAAA,YAAAA,EAAM,UAAW,UAAY,IAAO,EAC7C,CAAA,CACD,EAEKK,EAAaC,EAAeX,GAAIK,GAAA,YAAAA,EAAM,UAAW,SAAS,EAEhE,GAAIC,EACF,OACEM,MAAC,OAAI,UAAU,yCACb,eAACC,EAAA,CAAQ,UAAU,uCAAuC,CAAA,CAC5D,EAIJ,GAAIN,GAAS,CAACF,EACZ,OACES,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAA,EAAAA,KAAC,IAAA,CAAE,UAAU,eAAe,SAAA,CAAA,UAAQP,GAAA,YAAAA,EAAiB,UAAW,MAAA,EAAO,EACvEO,EAAAA,KAACC,EAAA,CAAK,GAAG,iBAAiB,UAAU,sEAClC,SAAA,CAAAH,EAAAA,IAACI,EAAA,CAAU,UAAU,cAAA,CAAe,EAAE,MAAA,CAAA,CAExC,CAAA,EACF,EAIJ,KAAM,CAAE,KAAAC,EAAM,OAAAC,EAAQ,oBAAAC,EAAqB,OAAAC,GAAWf,EAChDgB,EAAWD,IAAW,YAAc,IAAM,KAAK,MAAOD,EAAsBF,EAAK,OAAO,OAAU,GAAG,EAE3G,cACG,MAAA,CAEC,SAAA,CAAAH,EAAAA,KAAC,MAAA,CAAI,UAAU,OACb,SAAA,CAAAA,EAAAA,KAACC,EAAA,CACC,GAAG,iBACH,UAAU,0EAEV,SAAA,CAAAH,EAAAA,IAACI,EAAA,CAAU,UAAU,cAAA,CAAe,EAAE,MAAA,CAAA,CAAA,EAGxCF,EAAAA,KAAC,MAAA,CAAI,UAAU,mCACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAF,EAAAA,IAAC,KAAA,CAAG,UAAU,mCACX,SAAAV,EAAiBG,EAAK,cAAc,GAAKA,EAAK,cAAA,CACjD,EACAS,EAAAA,KAAC,IAAA,CAAE,UAAU,6BAA6B,SAAA,CAAA,YAC9BT,EAAK,UAAA,CAAA,CACjB,CAAA,EACF,EACAS,OAAC,QAAK,UAAWQ,EACf,6CACAF,IAAW,WAAa,4BACxBA,IAAW,aAAe,8BAC1BA,IAAW,UAAY,yBAAA,EAEtB,SAAA,CAAAA,IAAW,WAAa,MACxBA,IAAW,aAAe,MAC1BA,IAAW,UAAY,IAAA,CAAA,CAC1B,CAAA,CAAA,CACF,CAAA,EACF,EAGAN,EAAAA,KAAC,MAAA,CAAI,UAAU,sCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAF,EAAAA,IAAC,KAAA,CAAG,UAAU,oCAAoC,SAAA,OAAI,EACtDE,EAAAA,KAAC,MAAA,CAAI,UAAU,gDACZ,SAAA,CAAAT,EAAK,MACJS,EAAAA,KAAC,OAAA,CACC,UAAWQ,EACT,kEACAjB,EAAK,OAAS,aAAe,gCAAkC,yBAAA,EAGhE,SAAA,CAAAA,EAAK,OAAS,aAAeO,EAAAA,IAACW,EAAA,CAAI,UAAU,UAAU,EAAKX,EAAAA,IAACY,EAAA,CAAQ,UAAU,SAAA,CAAU,EACxFnB,EAAK,OAAS,aAAe,KAAO,IAAA,CAAA,CAAA,SAGxC,OAAA,CAAM,SAAA,CAAAgB,EAAS,GAAA,CAAA,CAAC,CAAA,CAAA,CACnB,CAAA,EACF,EACAT,EAAAA,IAAC,MAAA,CAAI,UAAU,sCACb,SAAAA,EAAAA,IAAC,MAAA,CACC,UAAWU,EACT,+CACAF,IAAW,YAAc,eAAiB,eAAA,EAE5C,MAAO,CAAE,MAAO,GAAGC,CAAQ,GAAA,CAAI,CAAA,EAEnC,EACAP,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAF,EAAAA,IAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,OAAI,EAC3CA,MAAC,OAAI,UAAU,sBACZ,WAAOP,EAAK,WAAY,gBAAgB,CAAA,CAC3C,CAAA,EACF,SACC,MAAA,CACC,SAAA,CAAAO,EAAAA,IAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,KAAE,EACzCE,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACZ,SAAA,CAAAK,GAAuBC,IAAW,YAAc,EAAI,GAAG,MAAIH,EAAK,OAAO,MAAA,CAAA,CAC1E,CAAA,EACF,SACC,MAAA,CACC,SAAA,CAAAL,MAAC,OAAI,UAAU,wBACZ,SAAAQ,IAAW,YAAc,OAAS,MACrC,EACAR,EAAAA,IAAC,OAAI,UAAU,sBACZ,aAAW,aAAeP,EAAK,aAC5BoB,EAAOpB,EAAK,aAAc,gBAAgB,EAC1CqB,EAAoBrB,EAAK,WAAY,CAAE,UAAW,GAAO,OAAQsB,CAAA,CAAM,CAAA,CAC7E,CAAA,CAAA,CACF,CAAA,EACF,GACEtB,EAAK,YAAcA,EAAK,oBACxBS,OAAC,MAAA,CAAI,UAAU,sDACZ,SAAA,CAAAT,EAAK,kBAAc,OAAA,CAAK,SAAA,CAAA,eAAYO,EAAAA,IAAC,OAAA,CAAK,UAAU,YAAa,WAAK,UAAA,CAAW,CAAA,CAAA,CAAO,EAAU,KAClGP,EAAK,kBACJS,EAAAA,KAAC,OAAA,CAAK,SAAA,CAAA,mBAAgBA,EAAAA,KAAC,OAAA,CAAK,UAAU,YAAa,SAAA,CAAA,OAAOT,EAAK,iBAAiB,EAAE,MAAM,EAAG,EAAE,EAAE,GAAA,CAAA,CAAC,CAAA,CAAA,CAAO,EACrG,IAAA,CAAA,CACN,CAAA,EAEJ,GAGEe,IAAW,WAAaV,EAAW,OAAS,IAC5CI,EAAAA,KAAC,MAAA,CAAI,UAAU,sCACb,SAAA,CAAAA,EAAAA,KAAC,KAAA,CAAG,UAAU,2DACZ,SAAA,CAAAF,EAAAA,IAACgB,EAAA,CAAS,UAAU,8BAAA,CAA+B,EAAE,QAErDd,EAAAA,KAAC,OAAA,CAAK,UAAU,6BAA6B,SAAA,CAAA,OAAKJ,EAAW,OAAO,KAAA,CAAA,CAAG,CAAA,EACzE,EACCA,EAAW,SAAW,EACrBE,EAAAA,IAAC,IAAA,CAAE,UAAU,wBACV,SAAAQ,IAAW,UAAY,gBAAkB,MAAA,CAC5C,QAEC,KAAA,CAAG,UAAU,qDACX,SAAAV,EAAW,IAAI,CAACmB,EAAIC,IACnBhB,EAAAA,KAAC,KAAA,CAAW,UAAU,yBACpB,SAAA,CAAAF,EAAAA,IAAC,QAAK,UAAU,mCACb,WAAOiB,EAAG,UAAW,UAAU,EAClC,EACAjB,MAAC,QAAK,UAAWU,EACf,uDACAO,EAAG,OAAS,eAAiB,gCAC7BA,EAAG,OAAS,gBAAkB,8BAC9BA,EAAG,OAAS,aAAe,4BAC3BA,EAAG,OAAS,SAAW,0BACvBA,EAAG,OAAS,QAAU,2BAAA,EAErB,WAAG,KACN,EACAf,EAAAA,KAAC,OAAA,CAAK,UAAU,yBACb,SAAA,CAAAe,EAAG,SAAW,SAASA,EAAG,QAAQ,IAAM,GACxCA,EAAG,QAAU,KAAK,UAAUA,EAAG,OAAO,EAAE,MAAM,EAAG,GAAG,EAAI,EAAA,CAAA,CAC3D,CAAA,CAAA,EAjBOC,CAkBT,CACD,CAAA,CACH,CAAA,EAEJ,EAIFhB,EAAAA,KAAC,MAAA,CAAI,UAAU,sCACb,SAAA,CAAAA,EAAAA,KAAC,KAAA,CAAG,UAAU,2DACZ,SAAA,CAAAF,EAAAA,IAACmB,EAAA,CAAY,UAAU,8BAAA,CAA+B,EAAE,cAAA,EAE1D,EACAnB,EAAAA,IAAC,IAAA,CAAE,UAAU,wCACV,WAAK,SAAA,CACR,CAAA,EACF,EAGAE,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAF,EAAAA,IAAC,KAAA,CAAG,UAAU,2CAA2C,SAAA,OAAI,EAE7DA,EAAAA,IAAC,OAAI,UAAU,YACZ,WAAK,OAAO,IAAI,CAACoB,EAAOC,IAAU,CACjC,MAAMC,EAAYhB,EAAO,KAAMiB,GAAMA,EAAE,cAAgBF,CAAK,EACtDG,EAAcH,EAAQd,GACzBc,IAAUd,GAAuBC,IAAW,cAC7Cc,GAAA,YAAAA,EAAW,UAAW,YAClBG,EAAYJ,IAAUd,GAAuBC,IAAW,YAAac,GAAA,YAAAA,EAAW,UAAW,UAC3FI,EAAYL,EAAQd,GAAuB,CAACkB,EAElD,OACEvB,EAAAA,KAAC,MAAA,CAAgB,UAAU,WAExB,SAAA,CAAAmB,EAAQhB,EAAK,OAAO,OAAS,GAC5BL,EAAAA,IAAC,MAAA,CACC,UAAWU,EACT,4CACAc,EAAc,eAAiB,aAAA,CACjC,CAAA,EAIJtB,EAAAA,KAAC,MAAA,CAAI,UAAU,aAEb,SAAA,CAAAF,MAAC,MAAA,CAAI,UAAU,gBACZ,SAAAwB,QACE,MAAA,CAAI,UAAU,qEACb,SAAAxB,EAAAA,IAAC2B,GAAY,UAAU,wBAAA,CAAyB,EAClD,EACEF,EACFzB,EAAAA,IAAC,MAAA,CAAI,UAAU,oEACb,SAAAA,EAAAA,IAACC,EAAA,CAAQ,UAAU,oCAAA,CAAqC,EAC1D,EAEAD,MAAC,MAAA,CAAI,UAAU,oEACb,SAAAA,MAAC4B,EAAA,CAAO,UAAU,uBAAA,CAAwB,EAC5C,EAEJ,EAGA5B,MAAC,OAAI,UAAWU,EACd,cACAgB,GAAa,YAAA,EAEb,SAAAxB,EAAAA,KAAC,MAAA,CAAI,UAAU,mDACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,KAAC,OAAA,CAAK,UAAU,sCAAsC,SAAA,CAAA,MAChDmB,EAAQ,EAAE,KAAGD,EAAM,EAAA,EACzB,EACAlB,OAAC,QAAK,UAAWQ,EACf,0CACAc,GAAe,8BACfC,GAAa,4BACbC,GAAa,2BAAA,EAEZ,SAAA,CAAAF,GAAe,MACfC,GAAa,MACbC,GAAa,KAAA,CAAA,CAChB,CAAA,EACF,GACCJ,GAAA,YAAAA,EAAW,cACVpB,OAAC,OAAA,CAAK,UAAU,wBACd,SAAA,CAAAF,EAAAA,IAAC6B,EAAA,CAAM,UAAU,qBAAA,CAAsB,GACrCP,EAAU,YAAc,KAAM,QAAQ,CAAC,EAAE,GAAA,CAAA,CAC7C,CAAA,EAEJ,EAEApB,EAAAA,KAAC,MAAA,CAAI,UAAU,qDACb,SAAA,CAAAF,EAAAA,IAAC8B,EAAA,CAAK,UAAU,SAAA,CAAU,SACzB,OAAA,CAAK,SAAA,CAAA,UAAO9B,EAAAA,IAAC,SAAA,CAAQ,SAAAoB,EAAM,KAAA,CAAM,CAAA,CAAA,CAAS,CAAA,EAC7C,EAEApB,EAAAA,IAAC,IAAA,CAAE,UAAU,oCACV,WAAM,UACT,EAEAE,EAAAA,KAAC,MAAA,CAAI,UAAU,8CACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,qDACb,SAAA,CAAAF,EAAAA,IAAC+B,EAAA,CAAS,UAAU,SAAA,CAAU,EAAE,QAAA,EAElC,EACA/B,EAAAA,IAAC,IAAA,CAAE,UAAU,wCACV,WAAM,MAAA,CACT,CAAA,EACF,EAECoB,EAAM,WAAaA,EAAM,UAAU,OAAS,GAC3ClB,EAAAA,KAAC,MAAA,CAAI,UAAU,OACb,SAAA,CAAAF,EAAAA,IAAC,MAAA,CAAI,UAAU,6BAA6B,SAAA,OAAI,EAChDA,EAAAA,IAAC,OAAI,UAAU,uBACZ,WAAM,UAAU,IAAI,CAACgC,EAAUd,IAC9BhB,EAAAA,KAAC,OAAA,CAEC,UAAU,2DAET,SAAA,CAAA8B,EAAS,SAAWA,EAAS,KAC7BA,EAAS,UAAY,IAAA,CAAA,EAJjBd,CAAA,CAMR,CAAA,CACH,CAAA,EACF,GAGDI,GAAA,YAAAA,EAAW,cACVpB,OAAC,UAAA,CAAQ,UAAU,+CACjB,SAAA,CAAAA,EAAAA,KAAC,UAAA,CAAQ,UAAU,oEACjB,SAAA,CAAAF,EAAAA,IAAC+B,EAAA,CAAS,UAAU,qBAAA,CAAsB,EAAE,OAAKT,EAAU,YAAY,OAAO,MAAA,EAChF,EACAtB,EAAAA,IAAC,MAAA,CAAI,UAAU,yFACxC,WAAU,WAAA,CACe,CAAA,EACF,GAGDsB,GAAA,YAAAA,EAAW,gBACVpB,OAAC,MAAA,CAAI,UAAU,mDACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,gEACb,SAAA,CAAAF,EAAAA,IAACmB,EAAA,CAAY,UAAU,SAAA,CAAU,EAAE,KAAA,EACrC,EACAnB,EAAAA,IAAC,MAAA,CAAI,UAAU,uDACxC,WAAU,aAAA,CACe,CAAA,EACF,GAGDsB,GAAA,YAAAA,EAAW,kBACVpB,OAAC,MAAA,CAAI,UAAU,iCAAiC,SAAA,CAAA,OAC1CF,EAAAA,IAAC,OAAA,CAAK,UAAU,YAAa,WAAU,eAAA,CAAgB,CAAA,CAAA,CAC7D,CAAA,CAAA,CAEJ,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CAAA,EA5HQqB,CA6HV,CAEJ,CAAC,CAAA,CACH,CAAA,CAAA,CACF,CAAA,EACF,CAEJ,CAWA,SAAStB,EAAekC,EAAiCC,EAAkB,CACzE,KAAM,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAiC,CAAA,CAAE,EACzDC,EAAQC,EAAAA,OAA2B,IAAI,EAE7CC,OAAAA,EAAAA,UAAU,IAAM,OACd,GAAI,CAACP,GAAe,CAACC,EAAS,EAC5BO,EAAAH,EAAM,UAAN,MAAAG,EAAe,QACfH,EAAM,QAAU,KAChB,MACF,CACA,MAAMI,EAAK,IAAI,YAAY,mDAAmDT,CAAW,EAAE,EAC3FK,EAAM,QAAUI,EAChB,MAAMC,EAAWC,GAAoB,CACnC,GAAI,CACF,MAAMnD,EAAO,KAAK,MAAMmD,EAAE,IAAI,EAC9BR,EAAUS,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMpD,CAAI,EAC3B,OAAIqD,EAAK,OAAS,KAAKA,EAAK,OAAO,EAAGA,EAAK,OAAS,GAAG,EAChDA,CACT,CAAC,CACH,MAAQ,CAAyB,CACnC,EACE,OAAC,cAAe,YAAa,eAAgB,OAAQ,OAAO,EAAY,QAAQC,GAAK,CACrFL,EAAG,iBAAiBK,EAAGJ,CAAwB,CACjD,CAAC,EACM,IAAM,CACXD,EAAG,MAAA,EACHJ,EAAM,QAAU,IAClB,CACF,EAAG,CAACL,EAAaC,CAAO,CAAC,EAElBC,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./react-vendor-CSp-GLFF.js";import{u as c}from"./query-C99w429o.js";import{b as m}from"./react-router-I-HqunH7.js";import{A as o,e as h,f as p,T as g}from"./lucide-
|
|
2
|
-
//# sourceMappingURL=Routing-
|
|
1
|
+
import{j as e}from"./react-vendor-CSp-GLFF.js";import{u as c}from"./query-C99w429o.js";import{b as m}from"./react-router-I-HqunH7.js";import{A as o,e as h,f as p,T as g}from"./lucide-BanPULT1.js";import"./vendor-CMMjVdZs.js";async function y(){const a=await fetch("/api/routing/stats?window=168");if(!a.ok)throw new Error("Failed to fetch");return a.json()}function w(){var n,i;const a=m(),{data:t,isLoading:x}=c({queryKey:["routing-stats"],queryFn:y,refetchInterval:1e4});if(x)return e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:"加载中..."});if(!t)return e.jsx("div",{className:"bg-white rounded-lg shadow p-6 text-red-600",children:"加载失败"});const d=t.obedienceRate!==null&&t.obedienceRate!==void 0?(t.obedienceRate*100).toFixed(1):"-",r=Object.entries(t.byAgent||{}).map(([s,l])=>({agent:s,...l,refusalRate:l.total>0?l.refused/l.total:null}));return e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900 mb-6",children:"Agent 路由"}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-4 mb-6",children:[e.jsxs("div",{className:"bg-white rounded-lg shadow p-5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm text-gray-500",children:"总路由次数"}),e.jsx(o,{className:"h-4 w-4 text-gray-400"})]}),e.jsx("div",{className:"text-2xl font-bold text-gray-900",children:t.total}),e.jsx("div",{className:"text-xs text-gray-500 mt-1",children:"最近 7 天"})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow p-5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm text-gray-500",children:"遵守率"}),e.jsx(h,{className:"h-4 w-4 text-green-500"})]}),e.jsxs("div",{className:"text-2xl font-bold text-gray-900",children:[d,"%"]}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[t.obeyedCount," / ",t.obeyedCount+t.refusedCount]})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow p-5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm text-gray-500",children:"违抗率"}),e.jsx(p,{className:"h-4 w-4 text-red-500"})]}),e.jsxs("div",{className:"text-2xl font-bold text-gray-900",children:[t.refusalRate!==null&&t.refusalRate!==void 0?(t.refusalRate*100).toFixed(1):"-","%"]}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[t.refusedCount," 次"]})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow p-5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm text-gray-500",children:"平均分类耗时"}),e.jsx(g,{className:"h-4 w-4 text-blue-500"})]}),e.jsx("div",{className:"text-2xl font-bold text-gray-900",children:(n=t.latency)!=null&&n.p50?`${t.latency.p50}ms`:"-"}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:["p95: ",(i=t.latency)!=null&&i.p95?`${t.latency.p95}ms`:"-"]})]})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow overflow-hidden",children:[e.jsx("div",{className:"px-6 py-4 border-b border-gray-200",children:e.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:"按 Agent 统计"})}),e.jsxs("table",{className:"min-w-full divide-y divide-gray-200",children:[e.jsx("thead",{className:"bg-gray-50",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"Agent"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"总次数"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"遵守"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"违抗"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"未判定"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase",children:"违抗率"})]})}),e.jsxs("tbody",{className:"bg-white divide-y divide-gray-200",children:[r.map(s=>e.jsxs("tr",{className:"hover:bg-gray-50 cursor-pointer",onClick:()=>a(`/execution-trace?agent=${encodeURIComponent(s.agent)}`),children:[e.jsx("td",{className:"px-6 py-3 whitespace-nowrap",children:e.jsx("span",{className:"text-sm font-medium text-indigo-600",children:s.agent})}),e.jsx("td",{className:"px-6 py-3 whitespace-nowrap text-sm text-gray-700",children:s.total}),e.jsx("td",{className:"px-6 py-3 whitespace-nowrap text-sm text-green-600",children:s.obeyed}),e.jsx("td",{className:"px-6 py-3 whitespace-nowrap text-sm text-red-600",children:s.refused}),e.jsx("td",{className:"px-6 py-3 whitespace-nowrap text-sm text-gray-400",children:s.unknown}),e.jsx("td",{className:"px-6 py-3 whitespace-nowrap text-sm text-gray-700",children:s.refusalRate!==null?`${(s.refusalRate*100).toFixed(1)}%`:"-"})]},s.agent)),r.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,className:"px-6 py-12 text-center text-gray-500",children:"暂无数据"})})]})]})]})]})}export{w as default};
|
|
2
|
+
//# sourceMappingURL=Routing-BW3eGD-8.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Routing-CFmM7JuB.js","sources":["../../src/pages/Routing.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { useNavigate } from 'react-router-dom'\nimport { TrendingUp, AlertTriangle, CheckCircle, Activity } from 'lucide-react'\n\ninterface AgentStats {\n total: number\n obeyed: number\n refused: number\n unknown: number\n}\n\ninterface RoutingStats {\n windowHours: number\n total: number\n forced: number\n obeyedCount: number\n refusedCount: number\n unknownCount: number\n obedienceRate: number | null\n refusalRate: number | null\n fallbackUsedCount?: number\n latency?: { p50?: number; p95?: number; p99?: number; count?: number }\n byAgent: Record<string, AgentStats>\n}\n\nasync function fetchRoutingStats(): Promise<RoutingStats> {\n const res = await fetch('/api/routing/stats?window=168')\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nexport default function Routing() {\n const navigate = useNavigate()\n const { data: stats, isLoading } = useQuery({\n queryKey: ['routing-stats'],\n queryFn: fetchRoutingStats,\n refetchInterval: 10000,\n })\n\n if (isLoading) {\n return <div className=\"bg-white rounded-lg shadow p-6\">加载中...</div>\n }\n\n if (!stats) {\n return <div className=\"bg-white rounded-lg shadow p-6 text-red-600\">加载失败</div>\n }\n\n const obediencePercent = stats.obedienceRate !== null && stats.obedienceRate !== undefined\n ? (stats.obedienceRate * 100).toFixed(1)\n : '-'\n\n // 把 byAgent 对象转成数组\n const byAgentArray = Object.entries(stats.byAgent || {}).map(([agent, s]) => ({\n agent,\n ...s,\n refusalRate: s.total > 0 ? s.refused / s.total : null,\n }))\n\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">Agent 路由</h1>\n\n {/* Stats cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-4 gap-4 mb-6\">\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">总路由次数</span>\n <Activity className=\"h-4 w-4 text-gray-400\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">{stats.total}</div>\n <div className=\"text-xs text-gray-500 mt-1\">最近 7 天</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">遵守率</span>\n <CheckCircle className=\"h-4 w-4 text-green-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">{obediencePercent}%</div>\n <div className=\"text-xs text-gray-500 mt-1\">{stats.obeyedCount} / {stats.obeyedCount + stats.refusedCount}</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">违抗率</span>\n <AlertTriangle className=\"h-4 w-4 text-red-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">\n {stats.refusalRate !== null && stats.refusalRate !== undefined\n ? (stats.refusalRate * 100).toFixed(1)\n : '-'}%\n </div>\n <div className=\"text-xs text-gray-500 mt-1\">{stats.refusedCount} 次</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">平均分类耗时</span>\n <TrendingUp className=\"h-4 w-4 text-blue-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">\n {stats.latency?.p50 ? `${stats.latency.p50}ms` : '-'}\n </div>\n <div className=\"text-xs text-gray-500 mt-1\">\n p95: {stats.latency?.p95 ? `${stats.latency.p95}ms` : '-'}\n </div>\n </div>\n </div>\n\n {/* By Agent table */}\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <div className=\"px-6 py-4 border-b border-gray-200\">\n <h3 className=\"text-lg font-semibold text-gray-900\">按 Agent 统计</h3>\n </div>\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Agent</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">总次数</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">遵守</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">违抗</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">未判定</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">违抗率</th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {byAgentArray.map((row) => (\n <tr\n key={row.agent}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => navigate(`/execution-trace?agent=${encodeURIComponent(row.agent)}`)}\n >\n <td className=\"px-6 py-3 whitespace-nowrap\">\n <span className=\"text-sm font-medium text-indigo-600\">{row.agent}</span>\n </td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-700\">{row.total}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-green-600\">{row.obeyed}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-red-600\">{row.refused}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-400\">{row.unknown}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-700\">\n {row.refusalRate !== null ? `${(row.refusalRate * 100).toFixed(1)}%` : '-'}\n </td>\n </tr>\n ))}\n {byAgentArray.length === 0 && (\n <tr>\n <td colSpan={6} className=\"px-6 py-12 text-center text-gray-500\">暂无数据</td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n )\n}\n"],"names":["fetchRoutingStats","res","Routing","navigate","useNavigate","stats","isLoading","useQuery","jsx","obediencePercent","byAgentArray","agent","s","jsxs","Activity","CheckCircle","AlertTriangle","TrendingUp","_a","_b","row"],"mappings":"iOAyBA,eAAeA,GAA2C,CACxD,MAAMC,EAAM,MAAM,MAAM,+BAA+B,EACvD,GAAI,CAACA,EAAI,GAAI,MAAM,IAAI,MAAM,iBAAiB,EAC9C,OAAOA,EAAI,KAAA,CACb,CAEA,SAAwBC,GAAU,SAChC,MAAMC,EAAWC,EAAA,EACX,CAAE,KAAMC,EAAO,UAAAC,CAAA,EAAcC,EAAS,CAC1C,SAAU,CAAC,eAAe,EAC1B,QAASP,EACT,gBAAiB,GAAA,CAClB,EAED,GAAIM,EACF,OAAOE,EAAAA,IAAC,MAAA,CAAI,UAAU,iCAAiC,SAAA,SAAM,EAG/D,GAAI,CAACH,EACH,OAAOG,EAAAA,IAAC,MAAA,CAAI,UAAU,8CAA8C,SAAA,OAAI,EAG1E,MAAMC,EAAmBJ,EAAM,gBAAkB,MAAQA,EAAM,gBAAkB,QAC5EA,EAAM,cAAgB,KAAK,QAAQ,CAAC,EACrC,IAGEK,EAAe,OAAO,QAAQL,EAAM,SAAW,EAAE,EAAE,IAAI,CAAC,CAACM,EAAOC,CAAC,KAAO,CAC5E,MAAAD,EACA,GAAGC,EACH,YAAaA,EAAE,MAAQ,EAAIA,EAAE,QAAUA,EAAE,MAAQ,IAAA,EACjD,EAEF,cACG,MAAA,CACC,SAAA,CAAAJ,EAAAA,IAAC,KAAA,CAAG,UAAU,wCAAwC,SAAA,WAAQ,EAG9DK,EAAAA,KAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAL,EAAAA,IAAC,OAAA,CAAK,UAAU,wBAAwB,SAAA,QAAK,EAC7CA,EAAAA,IAACM,EAAA,CAAS,UAAU,uBAAA,CAAwB,CAAA,EAC9C,EACAN,EAAAA,IAAC,MAAA,CAAI,UAAU,mCAAoC,WAAM,MAAM,EAC/DA,EAAAA,IAAC,MAAA,CAAI,UAAU,6BAA6B,SAAA,QAAA,CAAM,CAAA,EACpD,EAEAK,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAL,EAAAA,IAAC,OAAA,CAAK,UAAU,wBAAwB,SAAA,MAAG,EAC3CA,EAAAA,IAACO,EAAA,CAAY,UAAU,wBAAA,CAAyB,CAAA,EAClD,EACAF,EAAAA,KAAC,MAAA,CAAI,UAAU,mCAAoC,SAAA,CAAAJ,EAAiB,GAAA,EAAC,EACrEI,EAAAA,KAAC,MAAA,CAAI,UAAU,6BAA8B,SAAA,CAAAR,EAAM,YAAY,MAAIA,EAAM,YAAcA,EAAM,YAAA,CAAA,CAAa,CAAA,EAC5G,EAEAQ,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAL,EAAAA,IAAC,OAAA,CAAK,UAAU,wBAAwB,SAAA,MAAG,EAC3CA,EAAAA,IAACQ,EAAA,CAAc,UAAU,sBAAA,CAAuB,CAAA,EAClD,EACAH,EAAAA,KAAC,MAAA,CAAI,UAAU,mCACZ,SAAA,CAAAR,EAAM,cAAgB,MAAQA,EAAM,cAAgB,QAChDA,EAAM,YAAc,KAAK,QAAQ,CAAC,EACnC,IAAI,GAAA,EACV,EACAQ,EAAAA,KAAC,MAAA,CAAI,UAAU,6BAA8B,SAAA,CAAAR,EAAM,aAAa,IAAA,CAAA,CAAE,CAAA,EACpE,EAEAQ,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAL,EAAAA,IAAC,OAAA,CAAK,UAAU,wBAAwB,SAAA,SAAM,EAC9CA,EAAAA,IAACS,EAAA,CAAW,UAAU,uBAAA,CAAwB,CAAA,EAChD,EACAT,EAAAA,IAAC,MAAA,CAAI,UAAU,mCACZ,UAAAU,EAAAb,EAAM,UAAN,MAAAa,EAAe,IAAM,GAAGb,EAAM,QAAQ,GAAG,KAAO,IACnD,EACAQ,EAAAA,KAAC,MAAA,CAAI,UAAU,6BAA6B,SAAA,CAAA,SACpCM,EAAAd,EAAM,UAAN,MAAAc,EAAe,IAAM,GAAGd,EAAM,QAAQ,GAAG,KAAO,GAAA,CAAA,CACxD,CAAA,CAAA,CACF,CAAA,EACF,EAGAQ,EAAAA,KAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAL,EAAAA,IAAC,MAAA,CAAI,UAAU,qCACb,SAAAA,EAAAA,IAAC,MAAG,UAAU,sCAAsC,sBAAU,CAAA,CAChE,EACAK,EAAAA,KAAC,QAAA,CAAM,UAAU,sCACf,SAAA,CAAAL,MAAC,QAAA,CAAM,UAAU,aACf,SAAAK,EAAAA,KAAC,KAAA,CACC,SAAA,CAAAL,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,QAAK,EACrFA,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,MAAG,EACnFA,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,KAAE,EAClFA,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,KAAE,EAClFA,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,MAAG,EACnFA,EAAAA,IAAC,KAAA,CAAG,UAAU,kEAAkE,SAAA,KAAA,CAAG,CAAA,CAAA,CACrF,CAAA,CACF,EACAK,EAAAA,KAAC,QAAA,CAAM,UAAU,oCACd,SAAA,CAAAH,EAAa,IAAKU,GACjBP,EAAAA,KAAC,KAAA,CAEC,UAAU,kCACV,QAAS,IAAMV,EAAS,0BAA0B,mBAAmBiB,EAAI,KAAK,CAAC,EAAE,EAEjF,SAAA,CAAAZ,EAAAA,IAAC,KAAA,CAAG,UAAU,8BACZ,SAAAA,EAAAA,IAAC,QAAK,UAAU,sCAAuC,SAAAY,EAAI,KAAA,CAAM,CAAA,CACnE,EACAZ,EAAAA,IAAC,KAAA,CAAG,UAAU,oDAAqD,WAAI,MAAM,EAC7EA,EAAAA,IAAC,KAAA,CAAG,UAAU,qDAAsD,WAAI,OAAO,EAC/EA,EAAAA,IAAC,KAAA,CAAG,UAAU,mDAAoD,WAAI,QAAQ,EAC9EA,EAAAA,IAAC,KAAA,CAAG,UAAU,oDAAqD,WAAI,QAAQ,EAC/EA,EAAAA,IAAC,KAAA,CAAG,UAAU,oDACX,WAAI,cAAgB,KAAO,IAAIY,EAAI,YAAc,KAAK,QAAQ,CAAC,CAAC,IAAM,GAAA,CACzE,CAAA,CAAA,EAbKA,EAAI,KAAA,CAeZ,EACAV,EAAa,SAAW,GACvBF,EAAAA,IAAC,KAAA,CACC,SAAAA,EAAAA,IAAC,KAAA,CAAG,QAAS,EAAG,UAAU,uCAAuC,SAAA,MAAA,CAAI,CAAA,CACvE,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,EACF,CAEJ"}
|
|
1
|
+
{"version":3,"file":"Routing-BW3eGD-8.js","sources":["../../src/pages/Routing.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { useNavigate } from 'react-router-dom'\nimport { TrendingUp, AlertTriangle, CheckCircle, Activity } from 'lucide-react'\n\ninterface AgentStats {\n total: number\n obeyed: number\n refused: number\n unknown: number\n}\n\ninterface RoutingStats {\n windowHours: number\n total: number\n forced: number\n obeyedCount: number\n refusedCount: number\n unknownCount: number\n obedienceRate: number | null\n refusalRate: number | null\n fallbackUsedCount?: number\n latency?: { p50?: number; p95?: number; p99?: number; count?: number }\n byAgent: Record<string, AgentStats>\n}\n\nasync function fetchRoutingStats(): Promise<RoutingStats> {\n const res = await fetch('/api/routing/stats?window=168')\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nexport default function Routing() {\n const navigate = useNavigate()\n const { data: stats, isLoading } = useQuery({\n queryKey: ['routing-stats'],\n queryFn: fetchRoutingStats,\n refetchInterval: 10000,\n })\n\n if (isLoading) {\n return <div className=\"bg-white rounded-lg shadow p-6\">加载中...</div>\n }\n\n if (!stats) {\n return <div className=\"bg-white rounded-lg shadow p-6 text-red-600\">加载失败</div>\n }\n\n const obediencePercent = stats.obedienceRate !== null && stats.obedienceRate !== undefined\n ? (stats.obedienceRate * 100).toFixed(1)\n : '-'\n\n // 把 byAgent 对象转成数组\n const byAgentArray = Object.entries(stats.byAgent || {}).map(([agent, s]) => ({\n agent,\n ...s,\n refusalRate: s.total > 0 ? s.refused / s.total : null,\n }))\n\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">Agent 路由</h1>\n\n {/* Stats cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-4 gap-4 mb-6\">\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">总路由次数</span>\n <Activity className=\"h-4 w-4 text-gray-400\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">{stats.total}</div>\n <div className=\"text-xs text-gray-500 mt-1\">最近 7 天</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">遵守率</span>\n <CheckCircle className=\"h-4 w-4 text-green-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">{obediencePercent}%</div>\n <div className=\"text-xs text-gray-500 mt-1\">{stats.obeyedCount} / {stats.obeyedCount + stats.refusedCount}</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">违抗率</span>\n <AlertTriangle className=\"h-4 w-4 text-red-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">\n {stats.refusalRate !== null && stats.refusalRate !== undefined\n ? (stats.refusalRate * 100).toFixed(1)\n : '-'}%\n </div>\n <div className=\"text-xs text-gray-500 mt-1\">{stats.refusedCount} 次</div>\n </div>\n\n <div className=\"bg-white rounded-lg shadow p-5\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-gray-500\">平均分类耗时</span>\n <TrendingUp className=\"h-4 w-4 text-blue-500\" />\n </div>\n <div className=\"text-2xl font-bold text-gray-900\">\n {stats.latency?.p50 ? `${stats.latency.p50}ms` : '-'}\n </div>\n <div className=\"text-xs text-gray-500 mt-1\">\n p95: {stats.latency?.p95 ? `${stats.latency.p95}ms` : '-'}\n </div>\n </div>\n </div>\n\n {/* By Agent table */}\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <div className=\"px-6 py-4 border-b border-gray-200\">\n <h3 className=\"text-lg font-semibold text-gray-900\">按 Agent 统计</h3>\n </div>\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Agent</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">总次数</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">遵守</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">违抗</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">未判定</th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">违抗率</th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {byAgentArray.map((row) => (\n <tr\n key={row.agent}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => navigate(`/execution-trace?agent=${encodeURIComponent(row.agent)}`)}\n >\n <td className=\"px-6 py-3 whitespace-nowrap\">\n <span className=\"text-sm font-medium text-indigo-600\">{row.agent}</span>\n </td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-700\">{row.total}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-green-600\">{row.obeyed}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-red-600\">{row.refused}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-400\">{row.unknown}</td>\n <td className=\"px-6 py-3 whitespace-nowrap text-sm text-gray-700\">\n {row.refusalRate !== null ? `${(row.refusalRate * 100).toFixed(1)}%` : '-'}\n </td>\n </tr>\n ))}\n {byAgentArray.length === 0 && (\n <tr>\n <td colSpan={6} className=\"px-6 py-12 text-center text-gray-500\">暂无数据</td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n )\n}\n"],"names":["fetchRoutingStats","res","Routing","navigate","useNavigate","stats","isLoading","useQuery","jsx","obediencePercent","byAgentArray","agent","s","jsxs","Activity","CheckCircle","AlertTriangle","TrendingUp","_a","_b","row"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as s}from"./react-vendor-CSp-GLFF.js";import{c,L as l}from"./react-router-I-HqunH7.js";import{u as m}from"./query-C99w429o.js";import{d as o}from"./vendor-CMMjVdZs.js";import{j as n,h,F as g,i as j,a as p}from"./lucide-
|
|
2
|
-
//# sourceMappingURL=SessionDetail-
|
|
1
|
+
import{j as s}from"./react-vendor-CSp-GLFF.js";import{c,L as l}from"./react-router-I-HqunH7.js";import{u as m}from"./query-C99w429o.js";import{d as o}from"./vendor-CMMjVdZs.js";import{j as n,h,F as g,i as j,a as p}from"./lucide-BanPULT1.js";import{f as N}from"./date-fns-CZ_bHujz.js";async function f(a){const t=await fetch(`/api/sessions/${a}/detail`);if(!t.ok)throw new Error("Failed to fetch session detail");return t.json()}function L(){const{id:a}=c(),{data:t,isLoading:d,error:x}=m({queryKey:["session-detail",a],queryFn:()=>f(a),enabled:!!a});return d?s.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:s.jsx("p",{className:"text-gray-600",children:"加载中..."})}):x||!t?s.jsxs("div",{children:[s.jsxs(l,{to:"/sessions",className:"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4",children:[s.jsx(n,{className:"h-4 w-4 mr-1"}),"返回"]}),s.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:s.jsx("p",{className:"text-red-600",children:"加载失败"})})]}):s.jsxs("div",{children:[s.jsxs(l,{to:"/sessions",className:"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4",children:[s.jsx(n,{className:"h-4 w-4 mr-1"}),"返回会话列表"]}),s.jsxs("div",{className:"bg-white rounded-lg shadow p-6 mb-6",children:[s.jsx("h1",{className:"text-xl font-bold text-gray-900 mb-2",children:"会话详情"}),s.jsx("p",{className:"text-sm text-gray-500 font-mono mb-4",children:t.session.session_id}),s.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[s.jsxs("div",{children:[s.jsx("div",{className:"text-xs text-gray-500",children:"事件数"}),s.jsx("div",{className:"text-lg font-semibold",children:t.session.event_count})]}),s.jsxs("div",{children:[s.jsx("div",{className:"text-xs text-gray-500",children:"任务数"}),s.jsx("div",{className:"text-lg font-semibold",children:t.tasks.length})]}),s.jsxs("div",{children:[s.jsx("div",{className:"text-xs text-gray-500",children:"开始时间"}),s.jsx("div",{className:"text-sm",children:t.session.start_time?N(new Date(t.session.start_time),"MM-dd HH:mm:ss"):"-"})]})]})]}),s.jsx("div",{className:"space-y-4",children:t.tasks.map(e=>s.jsxs("div",{className:"bg-white rounded-lg shadow p-6",children:[s.jsxs("div",{className:"flex items-center justify-between mb-4",children:[s.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:e.title}),s.jsx("span",{className:o("px-2 py-1 text-xs font-medium rounded-full",e.status==="completed"&&"bg-green-100 text-green-700",e.status==="active"&&"bg-blue-100 text-blue-700",e.status==="abandoned"&&"bg-gray-100 text-gray-600"),children:e.status})]}),s.jsxs("div",{className:"grid grid-cols-3 gap-4 mb-4 text-sm",children:[s.jsxs("div",{children:[s.jsxs("div",{className:"text-xs text-gray-500 mb-1 flex items-center gap-1",children:[s.jsx(h,{className:"h-3 w-3"}),"工具使用"]}),s.jsx("div",{className:"space-y-1",children:Object.entries(e.summary.toolUsage).slice(0,5).map(([i,r])=>s.jsxs("div",{className:"flex justify-between text-xs",children:[s.jsx("span",{className:"text-gray-600",children:i}),s.jsx("span",{className:"font-mono text-gray-900",children:r})]},i))})]}),s.jsxs("div",{children:[s.jsxs("div",{className:"text-xs text-gray-500 mb-1 flex items-center gap-1",children:[s.jsx(g,{className:"h-3 w-3"}),"文件变更"]}),s.jsxs("div",{className:"text-xs text-gray-700",children:[e.summary.filesChanged.length," 个文件"]})]}),s.jsxs("div",{children:[s.jsxs("div",{className:"text-xs text-gray-500 mb-1 flex items-center gap-1",children:[s.jsx(j,{className:"h-3 w-3"}),"提交"]}),s.jsxs("div",{className:"text-xs text-gray-700",children:[e.summary.commits.length," 次"]})]})]}),e.prompts.length>0&&s.jsxs("div",{className:"border-t pt-3",children:[s.jsxs("div",{className:"text-xs text-gray-500 mb-2 flex items-center gap-1",children:[s.jsx(p,{className:"h-3 w-3"}),"用户提示"]}),s.jsx("div",{className:"space-y-2",children:e.prompts.slice(0,3).map((i,r)=>s.jsx("div",{className:"text-sm bg-gray-50 rounded p-2",children:s.jsx("p",{className:"text-gray-700 line-clamp-2",children:i.content})},r))})]})]},e.id))})]})}export{L as default};
|
|
2
|
+
//# sourceMappingURL=SessionDetail-Cbd7Jwox.js.map
|
package/dist/web/static/assets/{SessionDetail-DzTue2xK.js.map → SessionDetail-Cbd7Jwox.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionDetail-
|
|
1
|
+
{"version":3,"file":"SessionDetail-Cbd7Jwox.js","sources":["../../src/pages/SessionDetail.tsx"],"sourcesContent":["import { useParams, Link } from 'react-router-dom'\nimport { useQuery } from '@tanstack/react-query'\nimport { format } from 'date-fns'\nimport { ArrowLeft, MessageSquare, FileEdit, Terminal, GitCommit } from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface TaskDetail {\n id: string\n title: string\n start_time: string\n end_time?: string\n status: string\n event_count: number\n prompts: Array<{ timestamp: string; content: string }>\n events: Array<any>\n injections: Array<any>\n summary: {\n toolUsage: Record<string, number>\n filesChanged: string[]\n commits: Array<{ timestamp: string; message: string }>\n }\n}\n\ninterface SessionDetail {\n session: {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n }\n tasks: TaskDetail[]\n}\n\nasync function fetchSessionDetail(id: string): Promise<SessionDetail> {\n const res = await fetch(`/api/sessions/${id}/detail`)\n if (!res.ok) throw new Error('Failed to fetch session detail')\n return res.json()\n}\n\nexport default function SessionDetail() {\n const { id } = useParams<{ id: string }>()\n const { data, isLoading, error } = useQuery({\n queryKey: ['session-detail', id],\n queryFn: () => fetchSessionDetail(id!),\n enabled: !!id,\n })\n\n if (isLoading) {\n return (\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-gray-600\">加载中...</p>\n </div>\n )\n }\n\n if (error || !data) {\n return (\n <div>\n <Link to=\"/sessions\" className=\"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4\">\n <ArrowLeft className=\"h-4 w-4 mr-1\" />返回\n </Link>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败</p>\n </div>\n </div>\n )\n }\n\n return (\n <div>\n <Link to=\"/sessions\" className=\"inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4\">\n <ArrowLeft className=\"h-4 w-4 mr-1\" />返回会话列表\n </Link>\n\n <div className=\"bg-white rounded-lg shadow p-6 mb-6\">\n <h1 className=\"text-xl font-bold text-gray-900 mb-2\">\n 会话详情\n </h1>\n <p className=\"text-sm text-gray-500 font-mono mb-4\">{data.session.session_id}</p>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <div className=\"text-xs text-gray-500\">事件数</div>\n <div className=\"text-lg font-semibold\">{data.session.event_count}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">任务数</div>\n <div className=\"text-lg font-semibold\">{data.tasks.length}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm\">\n {data.session.start_time ? format(new Date(data.session.start_time), 'MM-dd HH:mm:ss') : '-'}\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"space-y-4\">\n {data.tasks.map((task) => (\n <div key={task.id} className=\"bg-white rounded-lg shadow p-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-gray-900\">{task.title}</h3>\n <span className={clsx(\n 'px-2 py-1 text-xs font-medium rounded-full',\n task.status === 'completed' && 'bg-green-100 text-green-700',\n task.status === 'active' && 'bg-blue-100 text-blue-700',\n task.status === 'abandoned' && 'bg-gray-100 text-gray-600'\n )}>\n {task.status}\n </span>\n </div>\n\n <div className=\"grid grid-cols-3 gap-4 mb-4 text-sm\">\n <div>\n <div className=\"text-xs text-gray-500 mb-1 flex items-center gap-1\">\n <Terminal className=\"h-3 w-3\" />工具使用\n </div>\n <div className=\"space-y-1\">\n {Object.entries(task.summary.toolUsage).slice(0, 5).map(([tool, count]) => (\n <div key={tool} className=\"flex justify-between text-xs\">\n <span className=\"text-gray-600\">{tool}</span>\n <span className=\"font-mono text-gray-900\">{count}</span>\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500 mb-1 flex items-center gap-1\">\n <FileEdit className=\"h-3 w-3\" />文件变更\n </div>\n <div className=\"text-xs text-gray-700\">{task.summary.filesChanged.length} 个文件</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500 mb-1 flex items-center gap-1\">\n <GitCommit className=\"h-3 w-3\" />提交\n </div>\n <div className=\"text-xs text-gray-700\">{task.summary.commits.length} 次</div>\n </div>\n </div>\n\n {task.prompts.length > 0 && (\n <div className=\"border-t pt-3\">\n <div className=\"text-xs text-gray-500 mb-2 flex items-center gap-1\">\n <MessageSquare className=\"h-3 w-3\" />用户提示\n </div>\n <div className=\"space-y-2\">\n {task.prompts.slice(0, 3).map((p, i) => (\n <div key={i} className=\"text-sm bg-gray-50 rounded p-2\">\n <p className=\"text-gray-700 line-clamp-2\">{p.content}</p>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n )\n}\n"],"names":["fetchSessionDetail","id","res","SessionDetail","useParams","data","isLoading","error","useQuery","jsx","jsxs","Link","ArrowLeft","format","task","clsx","Terminal","tool","count","FileEdit","GitCommit","MessageSquare","p","i"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e,r as o}from"./react-vendor-CSp-GLFF.js";import{u as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-
|
|
2
|
-
//# sourceMappingURL=Sessions-
|
|
1
|
+
import{j as e,r as o}from"./react-vendor-CSp-GLFF.js";import{u as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-DOUcx6m1.js";import{d as N}from"./vendor-CMMjVdZs.js";import{f as v,a as b,z as w}from"./date-fns-CZ_bHujz.js";import{h as S,F as C,i as _,a as f,D,A as E,g as F}from"./lucide-BanPULT1.js";import{S as q,g as M,d as z,a as I}from"./export-CbQTOt71.js";import{u as L}from"./index-CIRlno4E.js";import"./react-router-I-HqunH7.js";import"./syntax-highlighter-44FakypI.js";async function T(r){const a=await fetch(`/api/sessions/${r}/detail`);if(!a.ok)throw new Error("Failed to fetch session detail");return a.json()}function $({sessionId:r}){const{data:a,isLoading:l,error:m}=j({queryKey:["session-detail",r],queryFn:()=>T(r),enabled:!!r});return l?e.jsx("div",{className:"p-6 text-gray-500",children:"加载中..."}):m||!a?e.jsx("div",{className:"p-6 text-red-600",children:"加载失败"}):e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"Session ID"}),e.jsx("div",{className:"text-sm font-mono text-gray-900 break-all",children:a.session.session_id})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-4 pb-4 border-b border-gray-200",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500",children:"事件数"}),e.jsx("div",{className:"text-lg font-semibold",children:a.session.event_count})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500",children:"任务数"}),e.jsx("div",{className:"text-lg font-semibold",children:a.tasks.length})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500",children:"开始时间"}),e.jsx("div",{className:"text-sm",children:a.session.start_time?v(new Date(a.session.start_time),"MM-dd HH:mm:ss"):"-"})]})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-semibold text-gray-700 mb-3",children:"任务列表"}),e.jsx("div",{className:"space-y-3",children:a.tasks.map(s=>e.jsxs("div",{className:"border border-gray-200 rounded-lg p-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h4",{className:"text-sm font-semibold text-gray-900 flex-1 mr-2",children:s.title}),e.jsx("span",{className:N("px-2 py-0.5 text-xs font-medium rounded-full whitespace-nowrap",s.status==="completed"&&"bg-green-100 text-green-700",s.status==="active"&&"bg-blue-100 text-blue-700",s.status==="abandoned"&&"bg-gray-100 text-gray-600"),children:s.status})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3 mb-3 text-xs",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-500 mb-1 flex items-center gap-1",children:[e.jsx(S,{className:"h-3 w-3"}),"工具使用"]}),e.jsx("div",{className:"space-y-0.5",children:Object.entries(s.summary.toolUsage).slice(0,4).map(([i,c])=>e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:i}),e.jsx("span",{className:"font-mono text-gray-900",children:c})]},i))})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-500 mb-1 flex items-center gap-1",children:[e.jsx(C,{className:"h-3 w-3"}),"文件变更"]}),e.jsxs("div",{className:"text-gray-700",children:[s.summary.filesChanged.length," 个"]}),s.summary.filesChanged.slice(0,3).map(i=>e.jsx("div",{className:"text-gray-500 truncate text-[10px]",title:i,children:i.split("/").pop()},i))]}),e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-500 mb-1 flex items-center gap-1",children:[e.jsx(_,{className:"h-3 w-3"}),"提交"]}),e.jsxs("div",{className:"text-gray-700",children:[s.summary.commits.length," 次"]})]})]}),s.prompts.length>0&&e.jsxs("div",{className:"border-t border-gray-100 pt-3",children:[e.jsxs("div",{className:"text-xs text-gray-500 mb-2 flex items-center gap-1",children:[e.jsx(f,{className:"h-3 w-3"}),"用户提示 (",s.prompts.length,")"]}),e.jsxs("div",{className:"space-y-1.5",children:[s.prompts.slice(0,3).map((i,c)=>e.jsx("div",{className:"text-xs bg-gray-50 rounded p-2",children:e.jsx("p",{className:"text-gray-700 line-clamp-2",children:i.content})},c)),s.prompts.length>3&&e.jsxs("div",{className:"text-xs text-gray-400",children:["还有 ",s.prompts.length-3," 条..."]})]})]})]},s.id))})]})]})}async function k(){const r=await fetch("/api/sessions?limit=200");if(!r.ok)throw new Error("Failed to fetch sessions");return r.json()}function U(){const[r,a]=o.useState(null),[l,m]=o.useState(""),[s,i]=o.useState(!1),c=L(),{data:x,isLoading:u,error:h}=j({queryKey:["sessions"],queryFn:k,refetchInterval:1e4}),n=o.useMemo(()=>{if(!x)return[];if(!l.trim())return x;const t=l.toLowerCase();return x.filter(d=>{var g;return((g=d.first_prompt)==null?void 0:g.toLowerCase().includes(t))||d.session_id.toLowerCase().includes(t)})},[x,l]),p=t=>{const d=M();t==="json"?z(`sessions-${d}.json`,n):I(`sessions-${d}.csv`,n),c.success(`已导出 ${n.length} 个会话`),i(!1)};return u?e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900 mb-6",children:"会话"}),e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:e.jsx("p",{className:"text-gray-600",children:"加载中..."})})]}):h?e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900 mb-6",children:"会话"}),e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:e.jsxs("p",{className:"text-red-600",children:["加载失败: ",h.message]})})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-4 flex-wrap gap-3",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"会话"}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx(q,{value:l,onChange:m,placeholder:"搜索 session / 提示...",className:"w-64"}),e.jsxs("div",{className:"relative",children:[e.jsxs("button",{onClick:()=>i(t=>!t),disabled:n.length===0,className:"inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50",children:[e.jsx(D,{className:"h-4 w-4"}),"导出"]}),s&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"fixed inset-0 z-10",onClick:()=>i(!1)}),e.jsxs("div",{className:"absolute right-0 mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-20",children:[e.jsx("button",{onClick:()=>p("json"),className:"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50",children:"导出为 JSON"}),e.jsx("button",{onClick:()=>p("csv"),className:"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50",children:"导出为 CSV"})]})]})]})]})]}),e.jsxs("div",{className:"text-sm text-gray-500 mb-3",children:["显示 ",n.length," 个 ",l&&"(已过滤)"]}),e.jsx("div",{className:"bg-white rounded-lg shadow overflow-hidden",children:e.jsxs("table",{className:"min-w-full divide-y divide-gray-200",children:[e.jsx("thead",{className:"bg-gray-50",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",children:"Session ID"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",children:"首次提示"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",children:"事件数"}),e.jsx("th",{className:"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",children:"时间"})]})}),e.jsxs("tbody",{className:"bg-white divide-y divide-gray-200",children:[n.map(t=>e.jsxs("tr",{className:"hover:bg-gray-50 cursor-pointer",onClick:()=>a(t.session_id),children:[e.jsx("td",{className:"px-6 py-4 whitespace-nowrap",children:e.jsxs("div",{className:"flex items-center gap-2 text-sm text-indigo-600 font-mono",children:[e.jsx(f,{className:"h-4 w-4"}),t.session_id.slice(0,12),"..."]})}),e.jsx("td",{className:"px-6 py-4",children:e.jsx("p",{className:"text-sm text-gray-900 max-w-md truncate",children:t.first_prompt||"(无提示)"})}),e.jsx("td",{className:"px-6 py-4 whitespace-nowrap",children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-sm text-gray-600",children:[e.jsx(E,{className:"h-4 w-4"}),t.event_count]})}),e.jsx("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-gray-500",children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(F,{className:"h-4 w-4"}),t.start_time?b(new Date(t.start_time),{addSuffix:!0,locale:w}):"-"]})})]},t.session_id)),n.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:4,className:"px-6 py-12 text-center text-gray-500",children:l?"无匹配结果":"暂无会话记录"})})]})]})}),e.jsx(y,{open:r!==null,onClose:()=>a(null),title:"会话详情",width:"w-[800px]",children:r&&e.jsx($,{sessionId:r})})]})}export{U as default};
|
|
2
|
+
//# sourceMappingURL=Sessions-CFpFk-5I.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sessions-DOd65EkN.js","sources":["../../src/components/SessionDetailContent.tsx","../../src/pages/Sessions.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { format } from 'date-fns'\nimport { MessageSquare, FileEdit, Terminal, GitCommit } from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface TaskDetail {\n id: string\n title: string\n start_time: string\n end_time?: string\n status: string\n event_count: number\n prompts: Array<{ timestamp: string; content: string }>\n events: Array<any>\n injections: Array<any>\n summary: {\n toolUsage: Record<string, number>\n filesChanged: string[]\n commits: Array<{ timestamp: string; message: string }>\n }\n}\n\ninterface SessionDetailData {\n session: {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n }\n tasks: TaskDetail[]\n}\n\nasync function fetchSessionDetail(id: string): Promise<SessionDetailData> {\n const res = await fetch(`/api/sessions/${id}/detail`)\n if (!res.ok) throw new Error('Failed to fetch session detail')\n return res.json()\n}\n\ninterface Props {\n sessionId: string\n}\n\nexport default function SessionDetailContent({ sessionId }: Props) {\n const { data, isLoading, error } = useQuery({\n queryKey: ['session-detail', sessionId],\n queryFn: () => fetchSessionDetail(sessionId),\n enabled: !!sessionId,\n })\n\n if (isLoading) {\n return <div className=\"p-6 text-gray-500\">加载中...</div>\n }\n\n if (error || !data) {\n return <div className=\"p-6 text-red-600\">加载失败</div>\n }\n\n return (\n <div className=\"space-y-4\">\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">Session ID</div>\n <div className=\"text-sm font-mono text-gray-900 break-all\">{data.session.session_id}</div>\n </div>\n\n <div className=\"grid grid-cols-3 gap-4 pb-4 border-b border-gray-200\">\n <div>\n <div className=\"text-xs text-gray-500\">事件数</div>\n <div className=\"text-lg font-semibold\">{data.session.event_count}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">任务数</div>\n <div className=\"text-lg font-semibold\">{data.tasks.length}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm\">\n {data.session.start_time ? format(new Date(data.session.start_time), 'MM-dd HH:mm:ss') : '-'}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-3\">任务列表</h3>\n <div className=\"space-y-3\">\n {data.tasks.map((task) => (\n <div key={task.id} className=\"border border-gray-200 rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-3\">\n <h4 className=\"text-sm font-semibold text-gray-900 flex-1 mr-2\">{task.title}</h4>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded-full whitespace-nowrap',\n task.status === 'completed' && 'bg-green-100 text-green-700',\n task.status === 'active' && 'bg-blue-100 text-blue-700',\n task.status === 'abandoned' && 'bg-gray-100 text-gray-600'\n )}>\n {task.status}\n </span>\n </div>\n\n <div className=\"grid grid-cols-3 gap-3 mb-3 text-xs\">\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <Terminal className=\"h-3 w-3\" />工具使用\n </div>\n <div className=\"space-y-0.5\">\n {Object.entries(task.summary.toolUsage).slice(0, 4).map(([tool, count]) => (\n <div key={tool} className=\"flex justify-between\">\n <span className=\"text-gray-600\">{tool}</span>\n <span className=\"font-mono text-gray-900\">{count}</span>\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <FileEdit className=\"h-3 w-3\" />文件变更\n </div>\n <div className=\"text-gray-700\">{task.summary.filesChanged.length} 个</div>\n {task.summary.filesChanged.slice(0, 3).map(f => (\n <div key={f} className=\"text-gray-500 truncate text-[10px]\" title={f}>\n {f.split('/').pop()}\n </div>\n ))}\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <GitCommit className=\"h-3 w-3\" />提交\n </div>\n <div className=\"text-gray-700\">{task.summary.commits.length} 次</div>\n </div>\n </div>\n\n {task.prompts.length > 0 && (\n <div className=\"border-t border-gray-100 pt-3\">\n <div className=\"text-xs text-gray-500 mb-2 flex items-center gap-1\">\n <MessageSquare className=\"h-3 w-3\" />用户提示 ({task.prompts.length})\n </div>\n <div className=\"space-y-1.5\">\n {task.prompts.slice(0, 3).map((p, i) => (\n <div key={i} className=\"text-xs bg-gray-50 rounded p-2\">\n <p className=\"text-gray-700 line-clamp-2\">{p.content}</p>\n </div>\n ))}\n {task.prompts.length > 3 && (\n <div className=\"text-xs text-gray-400\">还有 {task.prompts.length - 3} 条...</div>\n )}\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { useState, useMemo } from 'react'\nimport { formatDistanceToNow } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport { MessageSquare, Clock, Activity, Download } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport SessionDetailContent from '../components/SessionDetailContent'\nimport SearchInput from '../components/SearchInput'\nimport { downloadJSON, downloadCSV, getTimestamp } from '../utils/export'\nimport { useToast } from '../components/Toast'\n\ninterface Session {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n}\n\nasync function fetchSessions(): Promise<Session[]> {\n const res = await fetch('/api/sessions?limit=200')\n if (!res.ok) throw new Error('Failed to fetch sessions')\n return res.json()\n}\n\nexport default function Sessions() {\n const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null)\n const [search, setSearch] = useState('')\n const [showExportMenu, setShowExportMenu] = useState(false)\n const toast = useToast()\n\n const { data: sessions, isLoading, error } = useQuery({\n queryKey: ['sessions'],\n queryFn: fetchSessions,\n refetchInterval: 10000,\n })\n\n const filteredSessions = useMemo(() => {\n if (!sessions) return []\n if (!search.trim()) return sessions\n const q = search.toLowerCase()\n return sessions.filter(s =>\n s.first_prompt?.toLowerCase().includes(q) ||\n s.session_id.toLowerCase().includes(q)\n )\n }, [sessions, search])\n\n const handleExport = (format: 'json' | 'csv') => {\n const ts = getTimestamp()\n if (format === 'json') {\n downloadJSON(`sessions-${ts}.json`, filteredSessions)\n } else {\n downloadCSV(`sessions-${ts}.csv`, filteredSessions)\n }\n toast.success(`已导出 ${filteredSessions.length} 个会话`)\n setShowExportMenu(false)\n }\n\n if (isLoading) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-gray-600\">加载中...</p>\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error).message}</p>\n </div>\n </div>\n )\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-4 flex-wrap gap-3\">\n <h1 className=\"text-2xl font-bold text-gray-900\">会话</h1>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder=\"搜索 session / 提示...\"\n className=\"w-64\"\n />\n <div className=\"relative\">\n <button\n onClick={() => setShowExportMenu(v => !v)}\n disabled={filteredSessions.length === 0}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n 导出\n </button>\n {showExportMenu && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setShowExportMenu(false)} />\n <div className=\"absolute right-0 mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-20\">\n <button\n onClick={() => handleExport('json')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 JSON\n </button>\n <button\n onClick={() => handleExport('csv')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 CSV\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"text-sm text-gray-500 mb-3\">\n 显示 {filteredSessions.length} 个 {search && `(已过滤)`}\n </div>\n\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n Session ID\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 首次提示\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 事件数\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 时间\n </th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {filteredSessions.map((session) => (\n <tr\n key={session.session_id}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedSessionId(session.session_id)}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2 text-sm text-indigo-600 font-mono\">\n <MessageSquare className=\"h-4 w-4\" />\n {session.session_id.slice(0, 12)}...\n </div>\n </td>\n <td className=\"px-6 py-4\">\n <p className=\"text-sm text-gray-900 max-w-md truncate\">\n {session.first_prompt || '(无提示)'}\n </p>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <span className=\"inline-flex items-center gap-1 text-sm text-gray-600\">\n <Activity className=\"h-4 w-4\" />\n {session.event_count}\n </span>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-4 w-4\" />\n {session.start_time\n ? formatDistanceToNow(new Date(session.start_time), {\n addSuffix: true,\n locale: zhCN,\n })\n : '-'}\n </span>\n </td>\n </tr>\n ))}\n {filteredSessions.length === 0 && (\n <tr>\n <td colSpan={4} className=\"px-6 py-12 text-center text-gray-500\">\n {search ? '无匹配结果' : '暂无会话记录'}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n\n <Drawer\n open={selectedSessionId !== null}\n onClose={() => setSelectedSessionId(null)}\n title=\"会话详情\"\n width=\"w-[800px]\"\n >\n {selectedSessionId && <SessionDetailContent sessionId={selectedSessionId} />}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSessionDetail","id","res","SessionDetailContent","sessionId","data","isLoading","error","useQuery","jsx","jsxs","format","task","clsx","Terminal","tool","count","FileEdit","f","GitCommit","MessageSquare","p","i","fetchSessions","Sessions","selectedSessionId","setSelectedSessionId","useState","search","setSearch","showExportMenu","setShowExportMenu","toast","useToast","sessions","filteredSessions","useMemo","q","s","_a","handleExport","ts","getTimestamp","downloadJSON","downloadCSV","SearchInput","v","Download","Fragment","session","Activity","Clock","formatDistanceToNow","zhCN","Drawer"],"mappings":"yeAiCA,eAAeA,EAAmBC,EAAwC,CACxE,MAAMC,EAAM,MAAM,MAAM,iBAAiBD,CAAE,SAAS,EACpD,GAAI,CAACC,EAAI,GAAI,MAAM,IAAI,MAAM,gCAAgC,EAC7D,OAAOA,EAAI,KAAA,CACb,CAMA,SAAwBC,EAAqB,CAAE,UAAAC,GAAoB,CACjE,KAAM,CAAE,KAAAC,EAAM,UAAAC,EAAW,MAAAC,CAAA,EAAUC,EAAS,CAC1C,SAAU,CAAC,iBAAkBJ,CAAS,EACtC,QAAS,IAAMJ,EAAmBI,CAAS,EAC3C,QAAS,CAAC,CAACA,CAAA,CACZ,EAED,OAAIE,EACKG,EAAAA,IAAC,MAAA,CAAI,UAAU,oBAAoB,SAAA,SAAM,EAG9CF,GAAS,CAACF,EACLI,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,SAAA,OAAI,EAI7CC,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,6BAA6B,SAAA,aAAU,QACrD,MAAA,CAAI,UAAU,4CAA6C,SAAAJ,EAAK,QAAQ,UAAA,CAAW,CAAA,EACtF,EAEAK,EAAAA,KAAC,MAAA,CAAI,UAAU,uDACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,MAAG,QACzC,MAAA,CAAI,UAAU,wBAAyB,SAAAJ,EAAK,QAAQ,WAAA,CAAY,CAAA,EACnE,SACC,MAAA,CACC,SAAA,CAAAI,EAAAA,IAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,MAAG,QACzC,MAAA,CAAI,UAAU,wBAAyB,SAAAJ,EAAK,MAAM,MAAA,CAAO,CAAA,EAC5D,SACC,MAAA,CACC,SAAA,CAAAI,EAAAA,IAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,OAAI,QAC1C,MAAA,CAAI,UAAU,UACZ,SAAAJ,EAAK,QAAQ,WAAaM,EAAO,IAAI,KAAKN,EAAK,QAAQ,UAAU,EAAG,gBAAgB,EAAI,GAAA,CAC3F,CAAA,CAAA,CACF,CAAA,EACF,SAEC,MAAA,CACC,SAAA,CAAAI,EAAAA,IAAC,KAAA,CAAG,UAAU,2CAA2C,SAAA,OAAI,EAC7DA,EAAAA,IAAC,MAAA,CAAI,UAAU,YACZ,SAAAJ,EAAK,MAAM,IAAKO,GACfF,EAAAA,KAAC,MAAA,CAAkB,UAAU,wCAC3B,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yCACb,SAAA,CAAAD,EAAAA,IAAC,KAAA,CAAG,UAAU,kDAAmD,SAAAG,EAAK,MAAM,EAC5EH,MAAC,QAAK,UAAWI,EACf,iEACAD,EAAK,SAAW,aAAe,8BAC/BA,EAAK,SAAW,UAAY,4BAC5BA,EAAK,SAAW,aAAe,2BAAA,EAE9B,WAAK,MAAA,CACR,CAAA,EACF,EAEAF,EAAAA,KAAC,MAAA,CAAI,UAAU,sCACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAD,EAAAA,IAACK,EAAA,CAAS,UAAU,SAAA,CAAU,EAAE,MAAA,EAClC,EACAL,EAAAA,IAAC,OAAI,UAAU,cACZ,gBAAO,QAAQG,EAAK,QAAQ,SAAS,EAAE,MAAM,EAAG,CAAC,EAAE,IAAI,CAAC,CAACG,EAAMC,CAAK,IACnEN,EAAAA,KAAC,MAAA,CAAe,UAAU,uBACxB,SAAA,CAAAD,EAAAA,IAAC,OAAA,CAAK,UAAU,gBAAiB,SAAAM,EAAK,EACtCN,EAAAA,IAAC,OAAA,CAAK,UAAU,0BAA2B,SAAAO,CAAA,CAAM,CAAA,CAAA,EAFzCD,CAGV,CACD,CAAA,CACH,CAAA,EACF,SACC,MAAA,CACC,SAAA,CAAAL,EAAAA,KAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAD,EAAAA,IAACQ,EAAA,CAAS,UAAU,SAAA,CAAU,EAAE,MAAA,EAClC,EACAP,EAAAA,KAAC,MAAA,CAAI,UAAU,gBAAiB,SAAA,CAAAE,EAAK,QAAQ,aAAa,OAAO,IAAA,EAAE,EAClEA,EAAK,QAAQ,aAAa,MAAM,EAAG,CAAC,EAAE,IAAIM,GACzCT,EAAAA,IAAC,MAAA,CAAY,UAAU,qCAAqC,MAAOS,EAChE,SAAAA,EAAE,MAAM,GAAG,EAAE,IAAA,CAAI,EADVA,CAEV,CACD,CAAA,EACH,SACC,MAAA,CACC,SAAA,CAAAR,EAAAA,KAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAD,EAAAA,IAACU,EAAA,CAAU,UAAU,SAAA,CAAU,EAAE,IAAA,EACnC,EACAT,EAAAA,KAAC,MAAA,CAAI,UAAU,gBAAiB,SAAA,CAAAE,EAAK,QAAQ,QAAQ,OAAO,IAAA,CAAA,CAAE,CAAA,CAAA,CAChE,CAAA,EACF,EAECA,EAAK,QAAQ,OAAS,GACrBF,EAAAA,KAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,qDACb,SAAA,CAAAD,EAAAA,IAACW,EAAA,CAAc,UAAU,SAAA,CAAU,EAAE,SAAOR,EAAK,QAAQ,OAAO,GAAA,EAClE,EACAF,EAAAA,KAAC,MAAA,CAAI,UAAU,cACZ,SAAA,CAAAE,EAAK,QAAQ,MAAM,EAAG,CAAC,EAAE,IAAI,CAACS,EAAGC,IAChCb,EAAAA,IAAC,OAAY,UAAU,iCACrB,eAAC,IAAA,CAAE,UAAU,6BAA8B,SAAAY,EAAE,OAAA,CAAQ,CAAA,EAD7CC,CAEV,CACD,EACAV,EAAK,QAAQ,OAAS,GACrBF,EAAAA,KAAC,MAAA,CAAI,UAAU,wBAAwB,SAAA,CAAA,MAAIE,EAAK,QAAQ,OAAS,EAAE,OAAA,CAAA,CAAK,CAAA,CAAA,CAE5E,CAAA,CAAA,CACF,CAAA,GA7DMA,EAAK,EA+Df,CACD,CAAA,CACH,CAAA,CAAA,CACF,CAAA,EACF,CAEJ,CCxIA,eAAeW,GAAoC,CACjD,MAAMrB,EAAM,MAAM,MAAM,yBAAyB,EACjD,GAAI,CAACA,EAAI,GAAI,MAAM,IAAI,MAAM,0BAA0B,EACvD,OAAOA,EAAI,KAAA,CACb,CAEA,SAAwBsB,GAAW,CACjC,KAAM,CAACC,EAAmBC,CAAoB,EAAIC,EAAAA,SAAwB,IAAI,EACxE,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAE,EACjC,CAACG,EAAgBC,CAAiB,EAAIJ,EAAAA,SAAS,EAAK,EACpDK,EAAQC,EAAA,EAER,CAAE,KAAMC,EAAU,UAAA5B,EAAW,MAAAC,CAAA,EAAUC,EAAS,CACpD,SAAU,CAAC,UAAU,EACrB,QAASe,EACT,gBAAiB,GAAA,CAClB,EAEKY,EAAmBC,EAAAA,QAAQ,IAAM,CACrC,GAAI,CAACF,EAAU,MAAO,CAAA,EACtB,GAAI,CAACN,EAAO,KAAA,EAAQ,OAAOM,EAC3B,MAAMG,EAAIT,EAAO,YAAA,EACjB,OAAOM,EAAS,OAAOI,GAAA,OACrB,QAAAC,EAAAD,EAAE,eAAF,YAAAC,EAAgB,cAAc,SAASF,KACvCC,EAAE,WAAW,YAAA,EAAc,SAASD,CAAC,EAAA,CAEzC,EAAG,CAACH,EAAUN,CAAM,CAAC,EAEfY,EAAgB7B,GAA2B,CAC/C,MAAM8B,EAAKC,EAAA,EACP/B,IAAW,OACbgC,EAAa,YAAYF,CAAE,QAASN,CAAgB,EAEpDS,EAAY,YAAYH,CAAE,OAAQN,CAAgB,EAEpDH,EAAM,QAAQ,OAAOG,EAAiB,MAAM,MAAM,EAClDJ,EAAkB,EAAK,CACzB,EAEA,OAAIzB,SAEC,MAAA,CACC,SAAA,CAAAG,EAAAA,IAAC,KAAA,CAAG,UAAU,wCAAwC,SAAA,KAAE,EACxDA,EAAAA,IAAC,OAAI,UAAU,iCACb,eAAC,IAAA,CAAE,UAAU,gBAAgB,SAAA,QAAA,CAAM,CAAA,CACrC,CAAA,EACF,EAIAF,SAEC,MAAA,CACC,SAAA,CAAAE,EAAAA,IAAC,KAAA,CAAG,UAAU,wCAAwC,SAAA,KAAE,QACvD,MAAA,CAAI,UAAU,iCACb,SAAAC,EAAAA,KAAC,IAAA,CAAE,UAAU,eAAe,SAAA,CAAA,SAAQH,EAAgB,OAAA,CAAA,CAAQ,CAAA,CAC9D,CAAA,EACF,SAKD,MAAA,CACC,SAAA,CAAAG,EAAAA,KAAC,MAAA,CAAI,UAAU,yDACb,SAAA,CAAAD,EAAAA,IAAC,KAAA,CAAG,UAAU,mCAAmC,SAAA,KAAE,EACnDC,EAAAA,KAAC,MAAA,CAAI,UAAU,oCACb,SAAA,CAAAD,EAAAA,IAACoC,EAAA,CACC,MAAOjB,EACP,SAAUC,EACV,YAAY,qBACZ,UAAU,MAAA,CAAA,EAEZnB,EAAAA,KAAC,MAAA,CAAI,UAAU,WACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CACC,QAAS,IAAMqB,EAAkBe,GAAK,CAACA,CAAC,EACxC,SAAUX,EAAiB,SAAW,EACtC,UAAU,qIAEV,SAAA,CAAA1B,EAAAA,IAACsC,EAAA,CAAS,UAAU,SAAA,CAAU,EAAE,IAAA,CAAA,CAAA,EAGjCjB,GACCpB,EAAAA,KAAAsC,WAAA,CACE,SAAA,CAAAvC,MAAC,OAAI,UAAU,qBAAqB,QAAS,IAAMsB,EAAkB,EAAK,EAAG,EAC7ErB,EAAAA,KAAC,MAAA,CAAI,UAAU,kFACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,QAAS,IAAM+B,EAAa,MAAM,EAClC,UAAU,4DACX,SAAA,UAAA,CAAA,EAGD/B,EAAAA,IAAC,SAAA,CACC,QAAS,IAAM+B,EAAa,KAAK,EACjC,UAAU,4DACX,SAAA,SAAA,CAAA,CAED,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,EACF,EAEA9B,EAAAA,KAAC,MAAA,CAAI,UAAU,6BAA6B,SAAA,CAAA,MACtCyB,EAAiB,OAAO,MAAIP,GAAU,OAAA,EAC5C,QAEC,MAAA,CAAI,UAAU,6CACb,SAAAlB,EAAAA,KAAC,QAAA,CAAM,UAAU,sCACf,SAAA,CAAAD,MAAC,QAAA,CAAM,UAAU,aACf,SAAAC,EAAAA,KAAC,KAAA,CACC,SAAA,CAAAD,EAAAA,IAAC,KAAA,CAAG,UAAU,iFAAiF,SAAA,aAE/F,EACAA,EAAAA,IAAC,KAAA,CAAG,UAAU,iFAAiF,SAAA,OAE/F,EACAA,EAAAA,IAAC,KAAA,CAAG,UAAU,iFAAiF,SAAA,MAE/F,EACAA,EAAAA,IAAC,KAAA,CAAG,UAAU,iFAAiF,SAAA,IAAA,CAE/F,CAAA,CAAA,CACF,CAAA,CACF,EACAC,EAAAA,KAAC,QAAA,CAAM,UAAU,oCACd,SAAA,CAAAyB,EAAiB,IAAKc,GACrBvC,EAAAA,KAAC,KAAA,CAEC,UAAU,kCACV,QAAS,IAAMgB,EAAqBuB,EAAQ,UAAU,EAEtD,SAAA,CAAAxC,EAAAA,IAAC,MAAG,UAAU,8BACZ,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,4DACb,SAAA,CAAAD,EAAAA,IAACW,EAAA,CAAc,UAAU,SAAA,CAAU,EAClC6B,EAAQ,WAAW,MAAM,EAAG,EAAE,EAAE,KAAA,CAAA,CACnC,CAAA,CACF,EACAxC,EAAAA,IAAC,KAAA,CAAG,UAAU,YACZ,SAAAA,EAAAA,IAAC,IAAA,CAAE,UAAU,0CACV,SAAAwC,EAAQ,cAAgB,OAAA,CAC3B,EACF,QACC,KAAA,CAAG,UAAU,8BACZ,SAAAvC,EAAAA,KAAC,OAAA,CAAK,UAAU,uDACd,SAAA,CAAAD,EAAAA,IAACyC,EAAA,CAAS,UAAU,SAAA,CAAU,EAC7BD,EAAQ,WAAA,CAAA,CACX,CAAA,CACF,QACC,KAAA,CAAG,UAAU,oDACZ,SAAAvC,EAAAA,KAAC,OAAA,CAAK,UAAU,iCACd,SAAA,CAAAD,EAAAA,IAAC0C,EAAA,CAAM,UAAU,SAAA,CAAU,EAC1BF,EAAQ,WACLG,EAAoB,IAAI,KAAKH,EAAQ,UAAU,EAAG,CAChD,UAAW,GACX,OAAQI,CAAA,CACT,EACD,GAAA,CAAA,CACN,CAAA,CACF,CAAA,CAAA,EA/BKJ,EAAQ,UAAA,CAiChB,EACAd,EAAiB,SAAW,GAC3B1B,EAAAA,IAAC,MACC,SAAAA,EAAAA,IAAC,KAAA,CAAG,QAAS,EAAG,UAAU,uCACvB,SAAAmB,EAAS,QAAU,SACtB,CAAA,CACF,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CACF,EAEAnB,EAAAA,IAAC6C,EAAA,CACC,KAAM7B,IAAsB,KAC5B,QAAS,IAAMC,EAAqB,IAAI,EACxC,MAAM,OACN,MAAM,YAEL,SAAAD,GAAqBhB,EAAAA,IAACN,EAAA,CAAqB,UAAWsB,CAAA,CAAmB,CAAA,CAAA,CAC5E,EACF,CAEJ"}
|
|
1
|
+
{"version":3,"file":"Sessions-CFpFk-5I.js","sources":["../../src/components/SessionDetailContent.tsx","../../src/pages/Sessions.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { format } from 'date-fns'\nimport { MessageSquare, FileEdit, Terminal, GitCommit } from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface TaskDetail {\n id: string\n title: string\n start_time: string\n end_time?: string\n status: string\n event_count: number\n prompts: Array<{ timestamp: string; content: string }>\n events: Array<any>\n injections: Array<any>\n summary: {\n toolUsage: Record<string, number>\n filesChanged: string[]\n commits: Array<{ timestamp: string; message: string }>\n }\n}\n\ninterface SessionDetailData {\n session: {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n }\n tasks: TaskDetail[]\n}\n\nasync function fetchSessionDetail(id: string): Promise<SessionDetailData> {\n const res = await fetch(`/api/sessions/${id}/detail`)\n if (!res.ok) throw new Error('Failed to fetch session detail')\n return res.json()\n}\n\ninterface Props {\n sessionId: string\n}\n\nexport default function SessionDetailContent({ sessionId }: Props) {\n const { data, isLoading, error } = useQuery({\n queryKey: ['session-detail', sessionId],\n queryFn: () => fetchSessionDetail(sessionId),\n enabled: !!sessionId,\n })\n\n if (isLoading) {\n return <div className=\"p-6 text-gray-500\">加载中...</div>\n }\n\n if (error || !data) {\n return <div className=\"p-6 text-red-600\">加载失败</div>\n }\n\n return (\n <div className=\"space-y-4\">\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">Session ID</div>\n <div className=\"text-sm font-mono text-gray-900 break-all\">{data.session.session_id}</div>\n </div>\n\n <div className=\"grid grid-cols-3 gap-4 pb-4 border-b border-gray-200\">\n <div>\n <div className=\"text-xs text-gray-500\">事件数</div>\n <div className=\"text-lg font-semibold\">{data.session.event_count}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">任务数</div>\n <div className=\"text-lg font-semibold\">{data.tasks.length}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm\">\n {data.session.start_time ? format(new Date(data.session.start_time), 'MM-dd HH:mm:ss') : '-'}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-3\">任务列表</h3>\n <div className=\"space-y-3\">\n {data.tasks.map((task) => (\n <div key={task.id} className=\"border border-gray-200 rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-3\">\n <h4 className=\"text-sm font-semibold text-gray-900 flex-1 mr-2\">{task.title}</h4>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded-full whitespace-nowrap',\n task.status === 'completed' && 'bg-green-100 text-green-700',\n task.status === 'active' && 'bg-blue-100 text-blue-700',\n task.status === 'abandoned' && 'bg-gray-100 text-gray-600'\n )}>\n {task.status}\n </span>\n </div>\n\n <div className=\"grid grid-cols-3 gap-3 mb-3 text-xs\">\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <Terminal className=\"h-3 w-3\" />工具使用\n </div>\n <div className=\"space-y-0.5\">\n {Object.entries(task.summary.toolUsage).slice(0, 4).map(([tool, count]) => (\n <div key={tool} className=\"flex justify-between\">\n <span className=\"text-gray-600\">{tool}</span>\n <span className=\"font-mono text-gray-900\">{count}</span>\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <FileEdit className=\"h-3 w-3\" />文件变更\n </div>\n <div className=\"text-gray-700\">{task.summary.filesChanged.length} 个</div>\n {task.summary.filesChanged.slice(0, 3).map(f => (\n <div key={f} className=\"text-gray-500 truncate text-[10px]\" title={f}>\n {f.split('/').pop()}\n </div>\n ))}\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <GitCommit className=\"h-3 w-3\" />提交\n </div>\n <div className=\"text-gray-700\">{task.summary.commits.length} 次</div>\n </div>\n </div>\n\n {task.prompts.length > 0 && (\n <div className=\"border-t border-gray-100 pt-3\">\n <div className=\"text-xs text-gray-500 mb-2 flex items-center gap-1\">\n <MessageSquare className=\"h-3 w-3\" />用户提示 ({task.prompts.length})\n </div>\n <div className=\"space-y-1.5\">\n {task.prompts.slice(0, 3).map((p, i) => (\n <div key={i} className=\"text-xs bg-gray-50 rounded p-2\">\n <p className=\"text-gray-700 line-clamp-2\">{p.content}</p>\n </div>\n ))}\n {task.prompts.length > 3 && (\n <div className=\"text-xs text-gray-400\">还有 {task.prompts.length - 3} 条...</div>\n )}\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { useState, useMemo } from 'react'\nimport { formatDistanceToNow } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport { MessageSquare, Clock, Activity, Download } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport SessionDetailContent from '../components/SessionDetailContent'\nimport SearchInput from '../components/SearchInput'\nimport { downloadJSON, downloadCSV, getTimestamp } from '../utils/export'\nimport { useToast } from '../components/Toast'\n\ninterface Session {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n}\n\nasync function fetchSessions(): Promise<Session[]> {\n const res = await fetch('/api/sessions?limit=200')\n if (!res.ok) throw new Error('Failed to fetch sessions')\n return res.json()\n}\n\nexport default function Sessions() {\n const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null)\n const [search, setSearch] = useState('')\n const [showExportMenu, setShowExportMenu] = useState(false)\n const toast = useToast()\n\n const { data: sessions, isLoading, error } = useQuery({\n queryKey: ['sessions'],\n queryFn: fetchSessions,\n refetchInterval: 10000,\n })\n\n const filteredSessions = useMemo(() => {\n if (!sessions) return []\n if (!search.trim()) return sessions\n const q = search.toLowerCase()\n return sessions.filter(s =>\n s.first_prompt?.toLowerCase().includes(q) ||\n s.session_id.toLowerCase().includes(q)\n )\n }, [sessions, search])\n\n const handleExport = (format: 'json' | 'csv') => {\n const ts = getTimestamp()\n if (format === 'json') {\n downloadJSON(`sessions-${ts}.json`, filteredSessions)\n } else {\n downloadCSV(`sessions-${ts}.csv`, filteredSessions)\n }\n toast.success(`已导出 ${filteredSessions.length} 个会话`)\n setShowExportMenu(false)\n }\n\n if (isLoading) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-gray-600\">加载中...</p>\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error).message}</p>\n </div>\n </div>\n )\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-4 flex-wrap gap-3\">\n <h1 className=\"text-2xl font-bold text-gray-900\">会话</h1>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder=\"搜索 session / 提示...\"\n className=\"w-64\"\n />\n <div className=\"relative\">\n <button\n onClick={() => setShowExportMenu(v => !v)}\n disabled={filteredSessions.length === 0}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n 导出\n </button>\n {showExportMenu && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setShowExportMenu(false)} />\n <div className=\"absolute right-0 mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-20\">\n <button\n onClick={() => handleExport('json')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 JSON\n </button>\n <button\n onClick={() => handleExport('csv')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 CSV\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"text-sm text-gray-500 mb-3\">\n 显示 {filteredSessions.length} 个 {search && `(已过滤)`}\n </div>\n\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n Session ID\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 首次提示\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 事件数\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 时间\n </th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {filteredSessions.map((session) => (\n <tr\n key={session.session_id}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedSessionId(session.session_id)}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2 text-sm text-indigo-600 font-mono\">\n <MessageSquare className=\"h-4 w-4\" />\n {session.session_id.slice(0, 12)}...\n </div>\n </td>\n <td className=\"px-6 py-4\">\n <p className=\"text-sm text-gray-900 max-w-md truncate\">\n {session.first_prompt || '(无提示)'}\n </p>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <span className=\"inline-flex items-center gap-1 text-sm text-gray-600\">\n <Activity className=\"h-4 w-4\" />\n {session.event_count}\n </span>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-4 w-4\" />\n {session.start_time\n ? formatDistanceToNow(new Date(session.start_time), {\n addSuffix: true,\n locale: zhCN,\n })\n : '-'}\n </span>\n </td>\n </tr>\n ))}\n {filteredSessions.length === 0 && (\n <tr>\n <td colSpan={4} className=\"px-6 py-12 text-center text-gray-500\">\n {search ? '无匹配结果' : '暂无会话记录'}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n\n <Drawer\n open={selectedSessionId !== null}\n onClose={() => setSelectedSessionId(null)}\n title=\"会话详情\"\n width=\"w-[800px]\"\n >\n {selectedSessionId && <SessionDetailContent sessionId={selectedSessionId} />}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSessionDetail","id","res","SessionDetailContent","sessionId","data","isLoading","error","useQuery","jsx","jsxs","format","task","clsx","Terminal","tool","count","FileEdit","f","GitCommit","MessageSquare","p","i","fetchSessions","Sessions","selectedSessionId","setSelectedSessionId","useState","search","setSearch","showExportMenu","setShowExportMenu","toast","useToast","sessions","filteredSessions","useMemo","q","s","_a","handleExport","ts","getTimestamp","downloadJSON","downloadCSV","SearchInput","v","Download","Fragment","session","Activity","Clock","formatDistanceToNow","zhCN","Drawer"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as c,j as e}from"./react-vendor-CSp-GLFF.js";import{b as p,u,c as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-
|
|
2
|
-
//# sourceMappingURL=Skills-
|
|
1
|
+
import{r as c,j as e}from"./react-vendor-CSp-GLFF.js";import{b as p,u,c as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-DOUcx6m1.js";import{M as w}from"./MarkdownRenderer-CCIz1MOz.js";import{u as b}from"./index-CIRlno4E.js";import{a as v}from"./auth-Bnf8ZcqN.js";import{b as g,t as N,X as k,u as S}from"./lucide-BanPULT1.js";import"./vendor-CMMjVdZs.js";import"./syntax-highlighter-44FakypI.js";import"./react-router-I-HqunH7.js";async function C(){const i=await fetch("/api/skills");if(!i.ok)throw new Error("Failed to fetch skills");const s=await i.json();return Array.isArray(s)?s:s.skills||[]}async function E(i){const s=await fetch(`/api/skills/${i}`);if(!s.ok)throw new Error("Failed to fetch");return s.json()}async function F(i,s){if(!(await v(`/api/skills/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:s})})).ok)throw new Error("Failed to save")}function R(){const i=p(),s=b(),{data:n,isLoading:f}=u({queryKey:["skills"],queryFn:C}),[r,x]=c.useState(null),[m,l]=c.useState(!1),[h,d]=c.useState(""),{data:t}=u({queryKey:["skill-detail",r],queryFn:()=>E(r),enabled:!!r});c.useEffect(()=>{t!=null&&t.content&&d(t.content),l(!1)},[t]);const o=j({mutationFn:a=>F(a.name,a.content),onSuccess:()=>{s.success("Skill 已保存"),l(!1),i.invalidateQueries({queryKey:["skill-detail",r]}),i.invalidateQueries({queryKey:["skills"]})},onError:a=>s.error(`保存失败: ${a.message}`)});return f?e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:"加载中..."}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-6",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"Skill 管理"}),e.jsxs("div",{className:"text-sm text-gray-500",children:["共 ",(n==null?void 0:n.length)||0," 个 Skill"]})]}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",children:n==null?void 0:n.map(a=>e.jsx("div",{className:"bg-white rounded-lg shadow p-5 hover:shadow-md transition-shadow cursor-pointer",onClick:()=>x(a.name),children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0",children:e.jsx(g,{className:"h-5 w-5 text-purple-600"})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"font-semibold text-gray-900 truncate",children:a.name}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 line-clamp-3",children:a.description||"(无描述)"})]})]})},a.name))}),e.jsx(y,{open:r!==null,onClose:()=>{x(null),l(!1)},title:`Skill: ${r}`,width:"w-[800px]",children:t&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(g,{className:"h-5 w-5 text-purple-600"}),e.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:t.name})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"描述"}),e.jsx("div",{className:"text-sm text-gray-800",children:t.description})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("div",{className:"text-xs text-gray-500",children:"完整内容 (Markdown)"}),e.jsx("div",{className:"flex items-center gap-2",children:m?e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>{l(!1),d(t.content||"")},className:"inline-flex items-center gap-1 text-xs text-gray-600 hover:text-gray-700",children:[e.jsx(k,{className:"h-3 w-3"}),"取消"]}),e.jsxs("button",{onClick:()=>o.mutate({name:t.name,content:h}),disabled:o.isPending,className:"inline-flex items-center gap-1 text-xs bg-indigo-600 text-white px-2 py-1 rounded hover:bg-indigo-700 disabled:bg-gray-400",children:[e.jsx(S,{className:"h-3 w-3"}),o.isPending?"保存中...":"保存"]})]}):e.jsxs("button",{onClick:()=>l(!0),className:"inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700",children:[e.jsx(N,{className:"h-3 w-3"}),"编辑"]})})]}),m?e.jsx("textarea",{value:h,onChange:a=>d(a.target.value),className:"w-full font-mono text-xs bg-gray-50 rounded p-3 border border-gray-300 focus:border-indigo-500 focus:outline-none",rows:20}):e.jsx("div",{className:"bg-white rounded p-3 border border-gray-200 max-h-[500px] overflow-y-auto",children:e.jsx(w,{content:t.content||""})}),o.isError&&e.jsxs("div",{className:"text-xs text-red-600 mt-1",children:["保存失败:",o.error.message]})]})]})})]})}export{R as default};
|
|
2
|
+
//# sourceMappingURL=Skills-BkbegTV1.js.map
|