Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HostAgentConfigDto } from "@superset/host-service/settings";
import type { HostAgentConfig } from "@superset/host-service/settings";
import { useQuery } from "@tanstack/react-query";
import { getHostServiceClientByUrl } from "renderer/lib/host-service-client";

Expand All @@ -17,7 +17,7 @@ export function useV2AgentConfigs(hostUrl: string | null) {
queryKey: [...V2_AGENT_CONFIGS_QUERY_KEY, hostUrl] as const,
enabled: !!hostUrl,
queryFn: () => {
if (!hostUrl) return [] as HostAgentConfigDto[];
if (!hostUrl) return [] as HostAgentConfig[];
return getHostServiceClientByUrl(
hostUrl,
).settings.agentConfigs.list.query();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type {
AgentPreset,
HostAgentConfigDto,
} from "@superset/host-service/settings";
import type { HostAgentConfig } from "@superset/host-service/settings";
import {
HOST_AGENT_PRESETS,
type HostAgentPreset,
} from "@superset/shared/host-agent-presets";
import { Skeleton } from "@superset/ui/skeleton";
import { toast } from "@superset/ui/sonner";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Bot } from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import {
V2_AGENT_CONFIGS_QUERY_KEY as QUERY_KEY,
useV2AgentConfigs,
Expand All @@ -16,10 +17,21 @@ import { useLocalHostService } from "renderer/routes/_authenticated/providers/Lo
import { AgentDetail } from "./components/AgentDetail";
import { AgentsSettingsSidebar } from "./components/AgentsSettingsSidebar";

const KNOWN_PRESETS: HostAgentPreset[] = HOST_AGENT_PRESETS.map((preset) => ({
...preset,
args: [...preset.args],
promptArgs: [...preset.promptArgs],
env: { ...preset.env },
}));

const DESCRIPTION_BY_PRESET_ID = new Map(
KNOWN_PRESETS.map((preset) => [preset.presetId, preset.description]),
);

interface V2AgentsSettingsProps {
/**
* Builtin preset id to pre-select on mount (e.g. "claude"). Resolved
* against `HostAgentConfigDto.presetId`. Consumed once per visit.
* against `HostAgentConfig.presetId`. Consumed once per visit.
*/
initialAgentPresetId?: string | null;
}
Expand All @@ -32,26 +44,16 @@ export function V2AgentsSettings({

const configsQuery = useV2AgentConfigs(activeHostUrl);

const presetsQuery = useQuery({
queryKey: [...QUERY_KEY, "presets", activeHostUrl] as const,
enabled: !!activeHostUrl,
queryFn: () => {
if (!activeHostUrl) return [] as AgentPreset[];
return getHostServiceClientByUrl(
activeHostUrl,
).settings.agentConfigs.listPresets.query();
},
});

const invalidate = () =>
queryClient.invalidateQueries({ queryKey: [...QUERY_KEY, activeHostUrl] });

const addMutation = useMutation({
mutationFn: (presetId: string) => {
mutationFn: (preset: HostAgentPreset) => {
if (!activeHostUrl) throw new Error("Host service is not available");
const { description: _description, ...body } = preset;
return getHostServiceClientByUrl(
activeHostUrl,
).settings.agentConfigs.add.mutate({ presetId });
).settings.agentConfigs.add.mutate(body);
},
onSuccess: (added) => {
invalidate();
Expand All @@ -72,7 +74,7 @@ export function V2AgentsSettings({
await queryClient.cancelQueries({
queryKey: [...QUERY_KEY, activeHostUrl],
});
const previous = queryClient.getQueryData<HostAgentConfigDto[]>([
const previous = queryClient.getQueryData<HostAgentConfig[]>([
...QUERY_KEY,
activeHostUrl,
]);
Expand All @@ -83,7 +85,7 @@ export function V2AgentsSettings({
const row = byId.get(id);
return row ? { ...row, order: index } : null;
})
.filter((row): row is HostAgentConfigDto => row !== null);
.filter((row): row is HostAgentConfig => row !== null);
queryClient.setQueryData([...QUERY_KEY, activeHostUrl], next);
}
return { previous };
Expand Down Expand Up @@ -113,11 +115,9 @@ export function V2AgentsSettings({
});

const configs = configsQuery.data ?? [];
const presets = presetsQuery.data ?? [];
const descriptionByPresetId = useMemo(
() =>
new Map(presets.map((preset) => [preset.presetId, preset.description])),
[presets],
const installedPresetIds = new Set(configs.map((row) => row.presetId));
const addablePresets = KNOWN_PRESETS.filter(
(preset) => !installedPresetIds.has(preset.presetId),
);

const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null);
Expand Down Expand Up @@ -164,10 +164,10 @@ export function V2AgentsSettings({
) : (
<AgentsSettingsSidebar
configs={configs}
presets={presets}
presets={addablePresets}
selectedAgentId={selectedAgentId}
onSelectAgent={setSelectedAgentId}
onAddAgent={(presetId) => addMutation.mutate(presetId)}
onAddAgent={(preset) => addMutation.mutate(preset)}
onReorder={(ids) => reorderMutation.mutate(ids)}
onResetToDefaults={() => resetMutation.mutate()}
isAdding={addMutation.isPending}
Expand All @@ -180,7 +180,7 @@ export function V2AgentsSettings({
key={selectedAgent.id}
config={selectedAgent}
description={
descriptionByPresetId.get(selectedAgent.presetId) ??
DESCRIPTION_BY_PRESET_ID.get(selectedAgent.presetId) ??
"Terminal agent launch configuration"
}
onChanged={invalidate}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type {
HostAgentConfigDto,
PromptTransport,
} from "@superset/host-service/settings";
import type { HostAgentConfig } from "@superset/host-service/settings";
import type { PromptTransport } from "@superset/shared/agent-prompt-launch";
import { Button } from "@superset/ui/button";
import { Input } from "@superset/ui/input";
import { Label } from "@superset/ui/label";
Expand All @@ -24,7 +22,7 @@ import {
} from "../../utils/argv";

interface AgentDetailProps {
config: HostAgentConfigDto;
config: HostAgentConfig;
description: string;
onChanged: () => void;
onDeleted: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ import {
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import type {
AgentPreset,
HostAgentConfigDto,
} from "@superset/host-service/settings";
import type { HostAgentConfig } from "@superset/host-service/settings";
import type { HostAgentPreset } from "@superset/shared/host-agent-presets";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -40,11 +38,11 @@ import {
} from "../../../../../components/SettingsListSidebar";

interface AgentsSettingsSidebarProps {
configs: HostAgentConfigDto[];
presets: AgentPreset[];
configs: HostAgentConfig[];
presets: HostAgentPreset[];
selectedAgentId: string | null;
onSelectAgent: (id: string) => void;
onAddAgent: (presetId: string) => void;
onAddAgent: (preset: HostAgentPreset) => void;
onReorder: (orderedIds: string[]) => void;
onResetToDefaults: () => void;
isAdding: boolean;
Expand Down Expand Up @@ -100,7 +98,7 @@ export function AgentsSettingsSidebar({
return (
<DropdownMenuItem
key={preset.presetId}
onSelect={() => onAddAgent(preset.presetId)}
onSelect={() => onAddAgent(preset)}
className="gap-2"
>
{icon ? (
Expand Down Expand Up @@ -161,7 +159,7 @@ export function AgentsSettingsSidebar({
}

interface AgentSidebarRowProps {
row: HostAgentConfigDto;
row: HostAgentConfig;
isActive: boolean;
onSelect: () => void;
isDark: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HostAgentConfigDto } from "@superset/host-service/settings";
import type { HostAgentConfig } from "@superset/host-service/settings";
import type { ExecutionMode, TerminalPreset } from "@superset/local-db";
import { Alert, AlertDescription } from "@superset/ui/alert";
import { Button } from "@superset/ui/button";
Expand Down Expand Up @@ -49,7 +49,7 @@ interface PresetEditorDialogProps {
* (read-only command + Open in Agents settings link). v1 callers omit
* this — no v1 row has agentId, so the linked branch stays dormant.
*/
agents?: HostAgentConfigDto[];
agents?: HostAgentConfig[];
open: boolean;
onOpenChange: (open: boolean) => void;
onDeletePreset: () => void;
Expand Down
36 changes: 36 additions & 0 deletions packages/cli/src/commands/agents/list/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { boolean, CLIError, string, table } from "@superset/cli-framework";
import { command } from "../../../lib/command";
import { requireHostTarget, resolveHostTarget } from "../../../lib/host-target";

export default command({
description: "List agents configured on a host",
options: {
host: string().desc("Target host machineId"),
local: boolean().desc("Target this machine"),
},
display: (data) =>
table(
(data ?? []) as Record<string, unknown>[],
["label", "presetId", "command", "id"],
["LABEL", "PRESET", "COMMAND", "ID"],
),
Comment on lines +7 to +16
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

--presets / listPresets flag is absent — contradicts the PR objective.

The PR description explicitly specifies that superset agents list --presets should switch to settings.agentConfigs.listPresets with columns LABEL | PRESET | COMMAND (no ID column). The current implementation only exposes settings.agentConfigs.list and has no --presets option at all.

The previous commit included a --presets flag but contained a bug where preset mode was inferred from rows[0], which broke on empty results. The current commit resolves the bug by removing the feature entirely. If --presets is intentionally deferred, please note it in the PR; otherwise the flag needs to be reinstated with the mode flag propagated explicitly from run to display.

💡 Proposed fix to reinstate --presets with explicit mode propagation
 export default command({
 	description: "List agents configured on a host",
 	options: {
 		host: string().desc("Target host machineId"),
 		local: boolean().desc("Target this machine"),
+		presets: boolean().desc("List available agent presets"),
 	},
-	display: (data) =>
-		table(
-			(data ?? []) as Record<string, unknown>[],
-			["label", "presetId", "command", "id"],
-			["LABEL", "PRESET", "COMMAND", "ID"],
-		),
+	display: (data) => {
+		const payload = (data ?? { rows: [], isPresetMode: false }) as {
+			rows: Record<string, unknown>[];
+			isPresetMode: boolean;
+		};
+		return payload.isPresetMode
+			? table(payload.rows, ["label", "presetId", "command"], ["LABEL", "PRESET", "COMMAND"])
+			: table(payload.rows, ["label", "presetId", "command", "id"], ["LABEL", "PRESET", "COMMAND", "ID"]);
+	},
 	run: async ({ ctx, options }) => {
 		// ... existing organizationId guard and host resolution ...

-		return target.client.settings.agentConfigs.list.query();
+		if (options.presets) {
+			const rows = await target.client.settings.agentConfigs.listPresets.query();
+			return { rows, isPresetMode: true };
+		}
+		const rows = await target.client.settings.agentConfigs.list.query();
+		return { rows, isPresetMode: false };
 	},
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
options: {
host: string().desc("Target host machineId"),
local: boolean().desc("Target this machine"),
},
display: (data) =>
table(
(data ?? []) as Record<string, unknown>[],
["label", "presetId", "command", "id"],
["LABEL", "PRESET", "COMMAND", "ID"],
),
export default command({
description: "List agents configured on a host",
options: {
host: string().desc("Target host machineId"),
local: boolean().desc("Target this machine"),
presets: boolean().desc("List available agent presets"),
},
display: (data) => {
const payload = (data ?? { rows: [], isPresetMode: false }) as {
rows: Record<string, unknown>[];
isPresetMode: boolean;
};
return payload.isPresetMode
? table(payload.rows, ["label", "presetId", "command"], ["LABEL", "PRESET", "COMMAND"])
: table(payload.rows, ["label", "presetId", "command", "id"], ["LABEL", "PRESET", "COMMAND", "ID"]);
},
run: async ({ ctx, options }) => {
// ... existing organizationId guard and host resolution ...
if (options.presets) {
const rows = await target.client.settings.agentConfigs.listPresets.query();
return { rows, isPresetMode: true };
}
const rows = await target.client.settings.agentConfigs.list.query();
return { rows, isPresetMode: false };
},
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/commands/agents/list/command.ts` around lines 7 - 16, Add a
new boolean option "presets" to the command's options (alongside host and
local), ensure run reads that flag and calls settings.agentConfigs.listPresets
when presets is true (falling back to settings.agentConfigs.list otherwise), and
pass the explicit presets mode into display so it does not infer mode from
results; update display/table invocation to use columns
["label","presetId","command"] with headers ["LABEL","PRESET","COMMAND"] when
presets is true (omit "id"), and keep the existing four-column display when
presets is false.

run: async ({ ctx, options }) => {
const organizationId = ctx.config.organizationId;
if (!organizationId) {
throw new CLIError("No active organization", "Run: superset auth login");
}

const hostId = requireHostTarget({
host: options.host ?? undefined,
local: options.local ?? undefined,
});

const target = resolveHostTarget({
requestedHostId: hostId,
organizationId,
userJwt: ctx.bearer,
});

return target.client.settings.agentConfigs.list.query();
},
});
2 changes: 1 addition & 1 deletion packages/cli/src/commands/agents/meta.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default {
description: "Run agents inside workspaces",
description: "Manage and run agents",
};
Loading
Loading