feat: unify queue execution handling with QueueItemExecutor and CLI execution settings

- Removed ad-hoc test scripts and temp files from project root
- Added QueueItemExecutor component to handle both session and orchestrator executions
- Created CliExecutionSettings component for shared execution parameter controls
- Introduced useCliSessionCore hook for managing CLI session lifecycle
- Merged buildQueueItemPrompt and buildQueueItemInstruction into a single function for context building
- Implemented Zustand store for queue execution state management
- Updated localization files for new execution features
This commit is contained in:
catlog22
2026-02-13 14:50:58 +08:00
parent af90069db2
commit ad5b35a1a5
17 changed files with 1466 additions and 36 deletions

View File

@@ -243,6 +243,13 @@ export type {
UseCliExecutionReturn,
} from './useCliExecution';
// ========== CLI Session Core ==========
export { useCliSessionCore } from './useCliSessionCore';
export type {
UseCliSessionCoreOptions,
UseCliSessionCoreReturn,
} from './useCliSessionCore';
// ========== Workspace Query Keys ==========
export {
useWorkspaceQueryKeys,

View File

@@ -0,0 +1,169 @@
// ========================================
// useCliSessionCore Hook
// ========================================
// Shared CLI session lifecycle management extracted from
// QueueExecuteInSession, QueueSendToOrchestrator, and IssueTerminalTab.
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
createCliSession,
fetchCliSessions,
type CliSession,
} from '@/lib/api';
import { useCliSessionStore } from '@/stores/cliSessionStore';
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface UseCliSessionCoreOptions {
/** When true, auto-select the most recent session on load. Defaults to true. */
autoSelectLast?: boolean;
/** Default resumeKey used when creating sessions via ensureSession/handleCreateSession. */
resumeKey?: string;
/** Additional createCliSession fields (cols, rows, tool, model). */
createSessionDefaults?: {
cols?: number;
rows?: number;
tool?: string;
model?: string;
};
}
export interface UseCliSessionCoreReturn {
/** Sorted list of CLI sessions (oldest first). */
sessions: CliSession[];
/** Currently selected session key. */
selectedSessionKey: string;
/** Setter for selected session key. */
setSelectedSessionKey: (key: string) => void;
/** Refresh sessions from the backend. */
refreshSessions: () => Promise<void>;
/** Return the current session key, creating one if none is selected. */
ensureSession: () => Promise<string>;
/** Explicitly create a new session and select it. */
handleCreateSession: () => Promise<void>;
/** True while sessions are being fetched. */
isLoading: boolean;
/** Last error message, or null. */
error: string | null;
/** Clear the current error. */
clearError: () => void;
}
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
export function useCliSessionCore(options: UseCliSessionCoreOptions = {}): UseCliSessionCoreReturn {
const {
autoSelectLast = true,
resumeKey,
createSessionDefaults,
} = options;
const projectPath = useWorkflowStore(selectProjectPath);
// Store selectors
const sessionsByKey = useCliSessionStore((s) => s.sessions);
const setSessions = useCliSessionStore((s) => s.setSessions);
const upsertSession = useCliSessionStore((s) => s.upsertSession);
// Derived sorted list (oldest first)
const sessions = useMemo(
() =>
Object.values(sessionsByKey).sort((a, b) =>
a.createdAt.localeCompare(b.createdAt)
),
[sessionsByKey]
);
// Local state
const [selectedSessionKey, setSelectedSessionKey] = useState<string>('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const clearError = useCallback(() => setError(null), []);
// ------- refreshSessions -------
const refreshSessions = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const r = await fetchCliSessions(projectPath || undefined);
setSessions(r.sessions as unknown as CliSession[]);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
} finally {
setIsLoading(false);
}
}, [projectPath, setSessions]);
// Fetch on mount / when projectPath changes
useEffect(() => {
void refreshSessions();
}, [refreshSessions]);
// Auto-select the last (most recent) session
useEffect(() => {
if (!autoSelectLast) return;
if (selectedSessionKey) return;
if (sessions.length === 0) return;
setSelectedSessionKey(sessions[sessions.length - 1]?.sessionKey ?? '');
}, [sessions, selectedSessionKey, autoSelectLast]);
// ------- ensureSession -------
const ensureSession = useCallback(async (): Promise<string> => {
if (selectedSessionKey) return selectedSessionKey;
if (!projectPath) throw new Error('No project path selected');
const created = await createCliSession(
{
workingDir: projectPath,
preferredShell: 'bash',
resumeKey,
...createSessionDefaults,
},
projectPath
);
upsertSession(created.session as unknown as CliSession);
setSelectedSessionKey(created.session.sessionKey);
return created.session.sessionKey;
}, [selectedSessionKey, projectPath, resumeKey, createSessionDefaults, upsertSession]);
// ------- handleCreateSession -------
const handleCreateSession = useCallback(async () => {
setError(null);
try {
if (!projectPath) throw new Error('No project path selected');
const created = await createCliSession(
{
workingDir: projectPath,
preferredShell: 'bash',
resumeKey,
...createSessionDefaults,
},
projectPath
);
upsertSession(created.session as unknown as CliSession);
setSelectedSessionKey(created.session.sessionKey);
// Refresh full list so store stays consistent
const r = await fetchCliSessions(projectPath || undefined);
setSessions(r.sessions as unknown as CliSession[]);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
}, [projectPath, resumeKey, createSessionDefaults, upsertSession, setSessions]);
return {
sessions,
selectedSessionKey,
setSelectedSessionKey,
refreshSessions,
ensureSession,
handleCreateSession,
isLoading,
error,
clearError,
};
}