mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-10 18:35:17 +00:00
* feat(composer): insert suggested commands into drafts * fix(composer): close suggestions after command insertion * fix(composer): place caret after inserted commands
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
import { useFetchQuery } from "@/data/query";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
import { useRetainedPanelActive } from "@/components/retained-panel";
|
|
import { useSessionStore } from "@/stores/session-store";
|
|
import { useShallow } from "zustand/shallow";
|
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
import {
|
|
agentCommandsQueryKey,
|
|
sessionCommandQueryPolicy,
|
|
type AgentCommandsDraftConfig,
|
|
} from "./cache";
|
|
|
|
export interface AgentSlashCommand {
|
|
name: string;
|
|
description: string;
|
|
argumentHint: string;
|
|
kind?: string;
|
|
}
|
|
|
|
export type DraftCommandConfig = AgentCommandsDraftConfig;
|
|
|
|
/** What a draft composer can list commands for, before an agent exists. */
|
|
export type DraftCommandTarget =
|
|
| { status: "ready"; config: DraftCommandConfig }
|
|
| { status: "needs-project" }
|
|
| { status: "needs-provider" };
|
|
|
|
interface ListAgentCommandsOptions {
|
|
agentId: string;
|
|
draftConfig?: DraftCommandConfig;
|
|
}
|
|
|
|
export interface AgentCommandsClient {
|
|
listCommands(options: ListAgentCommandsOptions): ReturnType<DaemonClient["listCommands"]>;
|
|
}
|
|
|
|
export async function fetchAgentCommands(input: {
|
|
client: AgentCommandsClient;
|
|
agentId: string;
|
|
draftConfig?: DraftCommandConfig;
|
|
}): Promise<AgentSlashCommand[]> {
|
|
const response = await input.client.listCommands({
|
|
agentId: input.agentId,
|
|
draftConfig: input.draftConfig,
|
|
});
|
|
if (response.error) throw new Error(response.error);
|
|
return response.commands as AgentSlashCommand[];
|
|
}
|
|
|
|
interface UseAgentCommandsQueryOptions {
|
|
serverId: string;
|
|
agentId: string;
|
|
enabled?: boolean;
|
|
draftConfig?: DraftCommandConfig;
|
|
}
|
|
|
|
export function useAgentCommandsQuery({
|
|
serverId,
|
|
agentId,
|
|
enabled = true,
|
|
draftConfig,
|
|
}: UseAgentCommandsQueryOptions) {
|
|
const { t } = useTranslation();
|
|
const retainedPanelActive = useRetainedPanelActive();
|
|
const queryEnabled = enabled && retainedPanelActive;
|
|
const client = useHostRuntimeClient(serverId);
|
|
const isConnected = useHostRuntimeIsConnected(serverId);
|
|
const { provider, cwd } = useSessionStore(
|
|
useShallow((state) => {
|
|
const agent =
|
|
state.sessions[serverId]?.agents?.get(agentId) ??
|
|
state.sessions[serverId]?.agentDetails?.get(agentId);
|
|
return { provider: agent?.provider, cwd: agent?.cwd };
|
|
}),
|
|
);
|
|
|
|
const query = useFetchQuery({
|
|
queryKey: agentCommandsQueryKey({ serverId, agentId, draftConfig }),
|
|
queryFn: async () => {
|
|
if (!client) {
|
|
throw new Error(t("common.errors.daemonClientUnavailable"));
|
|
}
|
|
return fetchAgentCommands({ client, agentId, draftConfig });
|
|
},
|
|
enabled: queryEnabled && !!client && isConnected && (!!agentId || !!draftConfig),
|
|
dataShape: "value",
|
|
meta: {
|
|
agentCommandProvider: draftConfig?.provider ?? provider,
|
|
agentCommandCwd: draftConfig?.cwd ?? cwd,
|
|
},
|
|
...(draftConfig
|
|
? { immutableWhen: () => true as const }
|
|
: { ...sessionCommandQueryPolicy, staleTimeMs: sessionCommandQueryPolicy.staleTime }),
|
|
subscribed: queryEnabled,
|
|
retry: 3,
|
|
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 5000),
|
|
});
|
|
|
|
// isPending is true when the query has never run yet (no cached data and not fetching)
|
|
// isLoading is true when fetching and no data yet
|
|
const isLoading = query.isPending || query.isLoading;
|
|
|
|
return {
|
|
commands: query.data ?? [],
|
|
isLoading,
|
|
isError: query.isError && query.data === undefined,
|
|
error: query.error,
|
|
};
|
|
}
|