This commit is contained in:
lda committed 2026-04-14 06:03:06 +07:00
1 parent 8948171ae7
commit f034fc5afd
11 files changed
+303 -18

No files matched your search

+65 -11
View File
@@ -1,6 +1,7 @@
import type { Agent } from "@/api";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { useState } from "react";
type Props = {
@@ -8,6 +9,10 @@ type Props = {
selectedAgentId: string;
onSelectAgent: (agentId: string) => void;
onRevokeAgent: (agentId: string) => Promise<boolean>;
onSetAgentNickname: (
agentId: string,
nickname: string | null,
) => Promise<boolean>;
};
export function AgentsPage({
@@ -15,10 +20,17 @@ export function AgentsPage({
selectedAgentId,
onSelectAgent,
onRevokeAgent,
onSetAgentNickname,
}: Props) {
const [busy, setBusy] = useState(false);
const [status, setStatus] = useState("");
const [error, setError] = useState("");
const [drafts, setDrafts] = useState<Record<string, string>>({});
function displayLabel(agent: Agent): string {
const nickname = agent.nickname?.trim();
return nickname ? nickname : agent.agent_id;
}
async function onRevoke(agentId: string) {
if (!window.confirm(`Revoke agent credentials for ${agentId}?`)) {
@@ -39,6 +51,25 @@ export function AgentsPage({
}
}
async function onSaveNickname(agent: Agent) {
const draft = drafts[agent.agent_id] ?? agent.nickname ?? "";
setBusy(true);
setStatus("");
setError("");
try {
const updated = await onSetAgentNickname(agent.agent_id, draft);
setStatus(
updated
? `Updated nickname for ${agent.agent_id}`
: `${agent.agent_id} not found`,
);
} catch (err) {
setError(String(err));
} finally {
setBusy(false);
}
}
return (
<Card>
<CardHeader>
@@ -55,24 +86,47 @@ export function AgentsPage({
variant={
selectedAgentId === agent.agent_id ? "secondary" : "outline"
}
className="flex h-auto flex-1 items-center justify-between px-3 py-2 text-left"
className="flex h-auto flex-1 items-center justify-between gap-3 px-3 py-2 text-left"
onClick={() => onSelectAgent(agent.agent_id)}
disabled={busy}
>
<span>{agent.agent_id}</span>
<span className="min-w-0 truncate">{displayLabel(agent)}</span>
<span className="text-xs text-muted-foreground">
{agent.connected ? "connected" : "offline"}
</span>
</Button>
<Button
variant="outline"
size="sm"
className="shrink-0 border-destructive/50 text-destructive hover:bg-destructive/10"
onClick={() => void onRevoke(agent.agent_id)}
disabled={busy}
>
Revoke
</Button>
<div className="grid min-w-52 gap-2">
<Input
value={drafts[agent.agent_id] ?? agent.nickname ?? ""}
onChange={(e) =>
setDrafts((prev) => ({
...prev,
[agent.agent_id]: e.target.value,
}))
}
placeholder="nickname (optional)"
disabled={busy}
/>
<div className="flex items-center justify-end gap-2">
<Button
variant="outline"
size="sm"
onClick={() => void onSaveNickname(agent)}
disabled={busy}
>
Save Name
</Button>
<Button
variant="outline"
size="sm"
className="shrink-0 border-destructive/50 text-destructive hover:bg-destructive/10"
onClick={() => void onRevoke(agent.agent_id)}
disabled={busy}
>
Revoke
</Button>
</div>
</div>
</div>
))}
{!agents.length && (
+8 -1
View File
@@ -26,6 +26,11 @@ export function CommandsPage({
onSelectAgent,
onAfterCommand,
}: Props) {
function displayAgentLabel(agent: Agent): string {
const nickname = agent.nickname?.trim();
return nickname ? nickname : agent.agent_id;
}
const [kind, setKind] = useState<CommandKind>("devs");
const [query, setQuery] = useState("");
const [output, setOutput] = useState("Select agent and run a command");
@@ -77,7 +82,9 @@ export function CommandsPage({
className={`size-2 shrink-0 rounded-full ${agent.connected ? "bg-emerald-500" : "bg-zinc-400"}`}
aria-hidden
/>
<span className="min-w-0 truncate">{agent.agent_id}</span>
<span className="min-w-0 truncate">
{displayAgentLabel(agent)}
</span>
<span className="shrink-0 text-xs text-muted-foreground">
{agent.connected ? "connected" : "offline"}
</span>
+9 -2
View File
@@ -44,6 +44,11 @@ export function DevicesPage({
onSelectAgent,
onAfterWake,
}: Props) {
function displayAgentLabel(agent: Agent): string {
const nickname = agent.nickname?.trim();
return nickname ? nickname : agent.agent_id;
}
const selectedAgent = agents.find(
(agent) => agent.agent_id === selectedAgentId,
);
@@ -351,7 +356,9 @@ export function DevicesPage({
) : null}
<span className="min-w-0 truncate">
{selectedAgentId
? selectedAgentId
? selectedAgent
? displayAgentLabel(selectedAgent)
: selectedAgentId
: "Select connected agent"}
</span>
{selectedAgent ? (
@@ -378,7 +385,7 @@ export function DevicesPage({
aria-hidden
/>
<span className="min-w-0 truncate">
{agent.agent_id}
{displayAgentLabel(agent)}
</span>
<span className="shrink-0 text-xs text-muted-foreground">
{agent.connected ? "connected" : "offline"}