import { defineStore } from 'pinia'; import { useUiStore } from './ui'; interface BackgroundCommand { command_id: string; status?: string; command?: string; notice_pending?: boolean; created_at?: number; updated_at?: number; finished_at?: number | null; timeout?: number; return_code?: number | null; } interface BackgroundCommandDetail extends BackgroundCommand { output?: string; message?: string; } interface BackgroundCommandState { commands: BackgroundCommand[]; pollTimer: ReturnType | null; detailPollTimer: ReturnType | null; activeCommand: BackgroundCommand | null; stoppingCommandIds: Record; activeDetail: BackgroundCommandDetail | null; detailLoading: boolean; detailError: string | null; } const TERMINAL_STATUSES = new Set(['completed', 'failed', 'timeout', 'cancelled']); export const useBackgroundCommandStore = defineStore('backgroundCommand', { state: (): BackgroundCommandState => ({ commands: [], pollTimer: null, detailPollTimer: null, activeCommand: null, stoppingCommandIds: {}, activeDetail: null, detailLoading: false, detailError: null }), actions: { async fetchCommands() { try { const resp = await fetch('/api/background_commands?limit=200'); if (!resp.ok) { throw new Error(await resp.text()); } const data = await resp.json(); if (data.success) { this.commands = Array.isArray(data.data) ? data.data : []; const activeId = this.activeCommand?.command_id; if (activeId) { const latest = this.commands.find((item) => item.command_id === activeId); if (latest) { this.activeCommand = { ...latest }; } } } } catch (error) { console.error('获取后台指令列表失败:', error); } }, startPolling() { if (this.pollTimer) { return; } const uiStore = useUiStore(); this.pollTimer = setInterval(() => { if (uiStore.panelMode === 'backgroundCommands') { this.fetchCommands(); } }, 5000); }, stopPolling() { if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; } }, openCommand(command: BackgroundCommand) { if (!command || !command.command_id) { return; } this.activeCommand = command; this.activeDetail = null; this.detailError = null; this.fetchCommandDetail(command.command_id); this.startDetailPolling(); }, closeCommand() { this.stopDetailPolling(); this.activeCommand = null; this.activeDetail = null; this.detailError = null; this.detailLoading = false; }, startDetailPolling() { if (this.detailPollTimer) { return; } this.detailPollTimer = setInterval(() => { const commandId = this.activeCommand?.command_id; if (commandId) { this.fetchCommandDetail(commandId); } }, 2000); }, stopDetailPolling() { if (this.detailPollTimer) { clearInterval(this.detailPollTimer); this.detailPollTimer = null; } }, async fetchCommandDetail(commandId: string) { if (!commandId) return; this.detailLoading = true; try { const resp = await fetch(`/api/background_commands/${encodeURIComponent(commandId)}`); if (!resp.ok) { throw new Error(await resp.text()); } const data = await resp.json(); if (data && data.success && data.data) { this.activeDetail = data.data; const current = this.commands.find((item) => item.command_id === commandId); if (current) { Object.assign(current, data.data); } if (this.activeCommand && this.activeCommand.command_id === commandId) { this.activeCommand = { ...this.activeCommand, ...data.data }; } const status = (data.data.status || '').toString(); if (TERMINAL_STATUSES.has(status)) { this.stopDetailPolling(); } } } catch (error: any) { this.detailError = error?.message || String(error); console.error('获取后台指令详情失败:', error); } finally { this.detailLoading = false; } }, async cancelCommand(commandId: string) { const normalizedId = (commandId || '').toString().trim(); if (!normalizedId) { return { success: false, error: 'command_id 不能为空' }; } this.stoppingCommandIds = { ...this.stoppingCommandIds, [normalizedId]: true }; try { const resp = await fetch( `/api/background_commands/${encodeURIComponent(normalizedId)}/cancel`, { method: 'POST' } ); const data = await resp.json().catch(() => ({})); if (!resp.ok || !data?.success) { throw new Error(data?.error || `HTTP ${resp.status}`); } await this.fetchCommands(); if (this.activeCommand?.command_id === normalizedId) { await this.fetchCommandDetail(normalizedId); } return { success: true, data: data?.data || null }; } catch (error: any) { const message = error?.message || String(error); return { success: false, error: message }; } finally { const next = { ...this.stoppingCommandIds }; delete next[normalizedId]; this.stoppingCommandIds = next; } }, async stopAllCommands(conversationId?: string): Promise<{ success: boolean; stoppedCount?: number; error?: string }> { try { const resp = await fetch('/api/background_commands/stop_all', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(conversationId ? { conversation_id: conversationId } : {}) }); const data = await resp.json().catch(() => ({})); if (!resp.ok || !data?.success) { throw new Error(data?.error || `HTTP ${resp.status}`); } await this.fetchCommands(); return { success: true, stoppedCount: data?.data?.stopped_count || 0 }; } catch (error: any) { return { success: false, error: error?.message || String(error) }; } } } });