import { defineStore } from 'pinia'; import { ref } from 'vue'; import type { CreateProjectRequest, CreateProjectResponse, CreateSessionRequest, HideDiscoveredResponse, SessionResponse, SessionSummary, SessionsListResponse } from '@arboretum/shared'; import { api } from '../lib/api'; import { wsClient, type SessionEvent } from '../lib/ws-client'; export const useSessionsStore = defineStore('sessions', () => { const sessions = ref([]); const loading = ref(false); const loadError = ref(null); // false (défaut) : les sessions masquées sont exclues ; true : on les inclut (mode « afficher les masquées »). const showHidden = ref(false); let unsubscribe: (() => void) | null = null; // vivantes d'abord, puis par date de création décroissante (même ordre que le serveur) function sorted(list: SessionSummary[]): SessionSummary[] { return [...list].sort((a, b) => Number(b.live) - Number(a.live) || b.createdAt.localeCompare(a.createdAt)); } function remove(id: string): void { const idx = sessions.value.findIndex((s) => s.id === id); if (idx >= 0) sessions.value.splice(idx, 1); } function upsert(session: SessionSummary): void { // session masquée et mode « afficher les masquées » inactif : on la retire de la liste. if (session.hidden && !showHidden.value) { remove(session.id); return; } const idx = sessions.value.findIndex((s) => s.id === session.id); if (idx >= 0) sessions.value.splice(idx, 1, session); else sessions.value.push(session); sessions.value = sorted(sessions.value); } function onEvent(e: SessionEvent): void { if (e.type === 'session_update') { upsert(e.session); return; } const current = sessions.value.find((s) => s.id === e.sessionId); if (!current) return; upsert({ ...current, status: 'exited', live: false, exitCode: e.exitCode, endedAt: new Date().toISOString(), clients: 0, }); } async function fetchSessions(): Promise { loading.value = true; loadError.value = null; try { const url = showHidden.value ? '/api/v1/sessions?includeHidden=true' : '/api/v1/sessions'; const res = await api.get(url); sessions.value = sorted(res.sessions); } catch (err) { loadError.value = err instanceof Error ? err.message : String(err); } finally { loading.value = false; } } async function setShowHidden(value: boolean): Promise { showHidden.value = value; await fetchSessions(); } // Masque une session découverte (retrait optimiste si on n'affiche pas les masquées). async function hideSession(id: string): Promise { await api.post<{ ok: true }>(`/api/v1/sessions/${id}/hide`); if (!showHidden.value) remove(id); } // Ré-affiche une session masquée. async function unhideSession(id: string): Promise { await api.delete<{ ok: true }>(`/api/v1/sessions/${id}/hide`); } // Masque tout l'historique de sessions externes visible. Renvoie le nombre masqué. async function hideDiscovered(): Promise { const res = await api.post('/api/v1/sessions/hide-discovered'); if (!showHidden.value) { sessions.value = sessions.value.filter((s) => !(s.source === 'discovered' && !s.hidden)); } return res.hidden; } function startRealtime(): void { unsubscribe ??= wsClient.subscribeSessions(onEvent); } function stopRealtime(): void { unsubscribe?.(); unsubscribe = null; } async function createSession(cwd: string, command: NonNullable): Promise { const body: CreateSessionRequest = { cwd, command }; const res = await api.post('/api/v1/sessions', body); upsert(res.session); return res.session; } // Crée un nouveau dossier de projet `/` côté serveur puis y lance une session. async function createProject(req: CreateProjectRequest): Promise { const res = await api.post('/api/v1/projects', req); upsert(res.session); return res; } async function killSession(id: string): Promise { await api.delete<{ ok: true }>(`/api/v1/sessions/${id}`); } // Reprise d'une session morte (nouveau PTY managé dans son cwd d'origine, côté serveur). async function resumeSession(id: string): Promise { const res = await api.post(`/api/v1/sessions/${id}/resume`); upsert(res.session); return res.session; } // Fork d'une session (vivante ou morte) — ne corrompt jamais l'originale. async function forkSession(id: string): Promise { const res = await api.post(`/api/v1/sessions/${id}/fork`); upsert(res.session); return res.session; } return { sessions, loading, loadError, showHidden, fetchSessions, setShowHidden, startRealtime, stopRealtime, createSession, createProject, killSession, resumeSession, forkSession, hideSession, unhideSession, hideDiscovered, }; });