Compare commits

..
Author SHA1 Message Date
ji ruo 524f8bc632 fix: 隐藏筛选与请求标签滚动条 2026-10-30 09:11:05 +08:00
ji ruo 255e11aca6 feat(web): show agent source in approval records 2026-10-30 09:01:57 +08:00
ji ruo 6ad055d24f fix(web): enable repeater request undo 2026-10-30 02:12:36 +08:00
RuoJi6 571b6116e0 fix: place resource status on dashboard header
* fix: place resource status on dashboard header

* fix: show resource metrics as progress bars

* fix: preview unsupported resource layout

* fix: disable non-Linux resource preview
2026-10-29 10:59:32 -07:00
5 changed files with 57 additions and 26 deletions

No files matched your search

@@ -923,7 +923,7 @@ export default function FindingsPage() {
})} })}
</div> </div>
<div className="flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1"> <div className="scrollbar-none flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1">
<InputGroup className="w-72 shrink-0"> <InputGroup className="w-72 shrink-0">
<InputGroupInput <InputGroupInput
type="search" type="search"
+25 -3
View File
@@ -65,6 +65,28 @@ function originLabel(row: InterceptApprovalRow) {
return "—"; return "—";
} }
// Background task runs carry a pool label such as "work#2 · #2770", while
// conversation-backed runs carry the agent key (for example "reporter").
// Present a stable, human-readable agent label in both cases.
function agentLabel(row: InterceptApprovalRow) {
const key = (row.conv_agent_key || "").trim().toLowerCase();
const labels: Record<string, string> = {
auto: "自动 Agent",
mainagent: "主 Agent",
planner: "规划 Agent",
worker: "执行 Worker",
reporter: "报告编写",
retester: "复测 Agent",
pentest: "渗透测试 Agent",
};
if (key && labels[key]) return labels[key];
const raw = (row.agent_name || "").trim();
if (/^work#\d+(?:\s|$)/i.test(raw)) return "执行 Worker";
const rawKey = raw.toLowerCase();
if (labels[rawKey]) return labels[rawKey];
return raw || "未知 Agent";
}
function ApprovalOrigin({ row, detail = false }: { row: InterceptApprovalRow; detail?: boolean }) { function ApprovalOrigin({ row, detail = false }: { row: InterceptApprovalRow; detail?: boolean }) {
const [locating, setLocating] = React.useState(false); const [locating, setLocating] = React.useState(false);
const label = detail && row.task_id ? `任务 ${row.task_id}` : originLabel(row); const label = detail && row.task_id ? `任务 ${row.task_id}` : originLabel(row);
@@ -454,7 +476,7 @@ export function ApprovalDetail({
<SideQuestionButton side={side} /> <SideQuestionButton side={side} />
</div> </div>
<CodeBlock <CodeBlock
label={`${current.agent_name || current.conv_agent_key || "Agent"} · 工具请求`} label={`${agentLabel(current)} · 工具请求`}
text={JSON.stringify(row.tool_input ?? {}, null, 2)} text={JSON.stringify(row.tool_input ?? {}, null, 2)}
/> />
{command ? ( {command ? (
@@ -726,8 +748,8 @@ function ApprovalTable({
<span className="truncate" title={originLabel(row)}> <span className="truncate" title={originLabel(row)}>
<ApprovalOrigin row={row} /> <ApprovalOrigin row={row} />
</span> </span>
<span className="truncate text-muted-foreground text-xs"> <span className="truncate text-muted-foreground text-xs" title={agentLabel(row)}>
{row.agent_name || row.conv_agent_key} Agent:{agentLabel(row)}
</span> </span>
</div> </div>
</TableCell> </TableCell>
+29 -20
View File
@@ -2,14 +2,8 @@
import * as React from "react"; import * as React from "react";
import { EditorState } from "@codemirror/state"; import { EditorState } from "@codemirror/state";
import { import { drawSelection, EditorView, highlightActiveLine, keymap, lineNumbers } from "@codemirror/view";
drawSelection, import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
EditorView,
highlightActiveLine,
keymap,
lineNumbers,
} from "@codemirror/view";
import { defaultKeymap, indentWithTab } from "@codemirror/commands";
import { defaultHighlightStyle, HighlightStyle, StreamLanguage, syntaxHighlighting } from "@codemirror/language"; import { defaultHighlightStyle, HighlightStyle, StreamLanguage, syntaxHighlighting } from "@codemirror/language";
import { tags } from "@lezer/highlight"; import { tags } from "@lezer/highlight";
@@ -37,17 +31,31 @@ const httpLanguage = StreamLanguage.define({
}, },
}); });
const packetTheme = EditorView.theme({ const packetTheme = EditorView.theme(
"&": { height: "100%", backgroundColor: "transparent", color: "var(--foreground)" }, {
".cm-scroller": { overflow: "auto", fontFamily: "var(--font-mono)", fontSize: "0.75rem", fontWeight: "500", lineHeight: "1.65rem" }, "&": { height: "100%", backgroundColor: "transparent", color: "var(--foreground)" },
".cm-content": { minHeight: "100%", padding: "0.5rem 0.5rem" }, ".cm-scroller": {
".cm-gutters": { backgroundColor: "transparent", color: "var(--muted-foreground)", border: "0", borderRight: "1px solid var(--border)" }, overflow: "auto",
".cm-lineNumbers .cm-gutterElement": { minWidth: "2rem", padding: "0 0.5rem" }, fontFamily: "var(--font-mono)",
".cm-activeLine": { backgroundColor: "transparent" }, fontSize: "0.75rem",
".cm-activeLineGutter": { backgroundColor: "transparent" }, fontWeight: "500",
".cm-selectionBackground, ::selection": { backgroundColor: "#c9d2e3" }, lineHeight: "1.65rem",
".cm-cursor": { borderLeftColor: "#222" }, },
}, { dark: false }); ".cm-content": { minHeight: "100%", padding: "0.5rem 0.5rem" },
".cm-gutters": {
backgroundColor: "transparent",
color: "var(--muted-foreground)",
border: "0",
borderRight: "1px solid var(--border)",
},
".cm-lineNumbers .cm-gutterElement": { minWidth: "2rem", padding: "0 0.5rem" },
".cm-activeLine": { backgroundColor: "transparent" },
".cm-activeLineGutter": { backgroundColor: "transparent" },
".cm-selectionBackground, ::selection": { backgroundColor: "#c9d2e3" },
".cm-cursor": { borderLeftColor: "#222" },
},
{ dark: false },
);
export function PacketCodeEditor({ export function PacketCodeEditor({
value, value,
@@ -76,7 +84,8 @@ export function PacketCodeEditor({
lineNumbers(), lineNumbers(),
drawSelection(), drawSelection(),
highlightActiveLine(), highlightActiveLine(),
keymap.of([...defaultKeymap, indentWithTab]), history(),
keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),
httpLanguage, httpLanguage,
syntaxHighlighting(defaultHighlightStyle), syntaxHighlighting(defaultHighlightStyle),
syntaxHighlighting(packetHighlight), syntaxHighlighting(packetHighlight),
@@ -112,7 +112,7 @@ export function RepeaterWorkspace() {
</div> </div>
</div> </div>
<div className="flex min-h-10 min-w-0 items-center rounded-md border bg-muted/20 p-1"> <div className="flex min-h-10 min-w-0 items-center rounded-md border bg-muted/20 p-1">
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto"> <div className="scrollbar-none flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
{workspace.tabs.map((tab) => { {workspace.tabs.map((tab) => {
const selected = tab.id === workspace.activeTabId; const selected = tab.id === workspace.activeTabId;
return ( return (
+1 -1
View File
@@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group" data-slot="input-group"
role="group" role="group"
className={cn( className={cn(
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", "group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
className className
)} )}
{...props} {...props}