chore: switch mergemaster to opus and add cargo fmt guidance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dave
2026-04-14 12:25:12 +00:00
parent badfabcf5e
commit 28adef9739
4 changed files with 223 additions and 40 deletions
+25
View File
@@ -8,6 +8,18 @@ export interface JoinedAgent {
label: string;
address: string;
registered_at: number;
/// Project this agent is assigned to, if any.
assigned_project?: string;
}
export interface GatewayProject {
name: string;
url: string;
}
export interface GatewayInfo {
active: string;
projects: GatewayProject[];
}
export interface GenerateTokenResponse {
@@ -61,4 +73,17 @@ export const gatewayApi = {
method: "DELETE",
});
},
/// Assign an agent to a project, or unassign it by passing null.
assignAgent(id: string, project: string | null): Promise<JoinedAgent> {
return gatewayRequest<JoinedAgent>(`/gateway/agents/${id}/assign`, {
method: "POST",
body: JSON.stringify({ project }),
});
},
/// Get the list of registered projects from the gateway.
getGatewayInfo(): Promise<GatewayInfo> {
return gatewayRequest<GatewayInfo>("/api/gateway");
},
};
+53 -3
View File
@@ -3,10 +3,10 @@
/// Provides:
/// - An "Add Agent" button that generates a one-time join token.
/// - Instructions for running a build agent with the token.
/// - A list of connected agents with per-agent "Remove" buttons.
/// - A list of connected agents with per-agent project assignment and "Remove" buttons.
import * as React from "react";
import { gatewayApi, type JoinedAgent } from "../api/gateway";
import { gatewayApi, type JoinedAgent, type GatewayProject } from "../api/gateway";
const { useCallback, useEffect, useState } = React;
@@ -90,12 +90,17 @@ function TokenDisplay({ token }: { token: string }) {
function AgentRow({
agent,
projects,
onRemove,
onAssign,
}: {
agent: JoinedAgent;
projects: GatewayProject[];
onRemove: (id: string) => void;
onAssign: (id: string, project: string | null) => void;
}) {
const registeredAt = new Date(agent.registered_at * 1000).toLocaleString();
const isAssigned = Boolean(agent.assigned_project);
return (
<div
@@ -116,9 +121,10 @@ function AgentRow({
width: "8px",
height: "8px",
borderRadius: "50%",
background: "#3fb950",
background: isAssigned ? "#3fb950" : "#6e7681",
flexShrink: 0,
}}
title={isAssigned ? "Assigned" : "Idle (unassigned)"}
/>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 600, color: "#e6edf3" }}>{agent.label}</div>
@@ -129,6 +135,29 @@ function AgentRow({
Registered {registeredAt}
</div>
</div>
<select
data-testid={`assign-agent-${agent.id}`}
value={agent.assigned_project ?? ""}
onChange={(e) =>
onAssign(agent.id, e.target.value === "" ? null : e.target.value)
}
style={{
fontSize: "0.8em",
padding: "4px 8px",
borderRadius: "4px",
border: "1px solid #30363d",
background: "#0d1117",
color: "#e6edf3",
cursor: "pointer",
}}
>
<option value=""> unassigned </option>
{projects.map((p) => (
<option key={p.name} value={p.name}>
{p.name}
</option>
))}
</select>
<button
type="button"
data-testid={`remove-agent-${agent.id}`}
@@ -152,6 +181,7 @@ function AgentRow({
/// Gateway management panel — rendered when running in `--gateway` mode.
export function GatewayPanel() {
const [agents, setAgents] = useState<JoinedAgent[]>([]);
const [projects, setProjects] = useState<GatewayProject[]>([]);
const [token, setToken] = useState<string | null>(null);
const [generating, setGenerating] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -161,6 +191,10 @@ export function GatewayPanel() {
.listAgents()
.then(setAgents)
.catch(() => setAgents([]));
gatewayApi
.getGatewayInfo()
.then((info) => setProjects(info.projects))
.catch(() => setProjects([]));
}, []);
const handleAddAgent = useCallback(async () => {
@@ -186,6 +220,20 @@ export function GatewayPanel() {
}
}, []);
const handleAssignAgent = useCallback(
async (id: string, project: string | null) => {
try {
const updated = await gatewayApi.assignAgent(id, project);
setAgents((prev) =>
prev.map((a) => (a.id === updated.id ? updated : a)),
);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
},
[],
);
return (
<div
style={{
@@ -273,7 +321,9 @@ export function GatewayPanel() {
<AgentRow
key={agent.id}
agent={agent}
projects={projects}
onRemove={handleRemoveAgent}
onAssign={handleAssignAgent}
/>
))}
</div>