feat: add deepseek-harness workbench plugin and console diagnosis API
- console: GET /api/workflows/{id}, /runs/{run_id}, /runs/{run_id}/diagnosis
(journal trace + sanitized bounded log tails, workflow/run pairing enforced)
- workbench/: dsh 宿主插件(7 个工作流工具、中文系统提示词、失败监控器、
本机回环桥接服务)+ 侧边栏面板客户端包(sidebar.footer.action 与
shell.overlay 追加插槽)+ 降级独立面板 + 一键启动脚本
- adapters/browser: 收敛 looks_like_login_url 到共享层,修复
jd_main_image_collector 对 gyxx_flow.accounts 的越层导入
- tests: 新端点覆盖;replay_policy 断言对齐已迁移的 catalog(repeatable)
This commit is contained in:
1 parent
b124d757b0
commit
01218b2907
26 files changed
+4347
-8
No files matched your search
@@ -0,0 +1,74 @@
|
||||
/** 失败告警浮层:轮询桥接告警,新失败弹出 toast,可一键发起诊断会话。 */
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { useSyncExternalStore } from 'react'
|
||||
import { getState, subscribe, update, dismissToast, pushToast } from './store.js'
|
||||
import { api } from './api.js'
|
||||
|
||||
function useWorkbench() {
|
||||
return useSyncExternalStore(subscribe, getState)
|
||||
}
|
||||
|
||||
export function Toasts() {
|
||||
const s = useWorkbench()
|
||||
|
||||
useEffect(() => {
|
||||
let stopped = false
|
||||
let timer = null
|
||||
const poll = async () => {
|
||||
try {
|
||||
const payload = await api.alerts()
|
||||
const fresh = (payload.alerts ?? []).filter(
|
||||
(alert) => !alert.seen && !getState().seenAlertIds[alert.id],
|
||||
)
|
||||
if (fresh.length) {
|
||||
const seen = { ...getState().seenAlertIds }
|
||||
for (const alert of fresh) {
|
||||
seen[alert.id] = true
|
||||
pushToast({ kind: 'failed', text: `工作流「${alert.name}」运行失败`, alert })
|
||||
}
|
||||
update({ seenAlertIds: seen })
|
||||
}
|
||||
} catch {
|
||||
// 桥接离线时静默
|
||||
}
|
||||
if (!stopped) timer = setTimeout(poll, 30_000)
|
||||
}
|
||||
timer = setTimeout(poll, 8_000)
|
||||
return () => {
|
||||
stopped = true
|
||||
if (timer) clearTimeout(timer)
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!s.toasts.length) return null
|
||||
return (
|
||||
<div className="gyxxwb-toasts">
|
||||
{s.toasts.map((toast) => (
|
||||
<div key={toast.id} className={`gyxxwb-toast gyxxwb-toast-${toast.kind}`}>
|
||||
<span className="gyxxwb-toast-text">{toast.text}</span>
|
||||
{toast.alert && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await api.ask({ workflow_id: toast.alert.workflow_id, action: 'diagnose' })
|
||||
await api.markAlertSeen(toast.alert.id).catch(() => {})
|
||||
pushToast({ kind: 'ok', text: '已创建诊断会话,请在左侧会话列表查看' })
|
||||
} catch (error) {
|
||||
pushToast({ kind: 'error', text: `创建会话失败:${error.message}` })
|
||||
}
|
||||
dismissToast(toast.id)
|
||||
}}
|
||||
>
|
||||
立即诊断
|
||||
</button>
|
||||
)}
|
||||
<button type="button" title="关闭" onClick={() => dismissToast(toast.id)}>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user