refactor(store): split gatewayStore into specialized domain stores

Major restructuring:
- Split monolithic gatewayStore into 5 focused stores:
  - connectionStore: WebSocket connection and gateway lifecycle
  - configStore: quickConfig, workspaceInfo, MCP services
  - agentStore: clones, usage stats, agent management
  - handStore: hands, approvals, triggers, hand runs
  - workflowStore: workflows, workflow runs, execution

- Update all components to use new stores with selector pattern
- Remove
This commit is contained in:
iven
2026-03-20 22:14:13 +08:00
parent 6f72442531
commit 1cf3f585d3
43 changed files with 2826 additions and 3103 deletions

View File

@@ -6,8 +6,7 @@
*
* The coordinator:
* 1. Injects the shared client into all stores
* 2. Provides a composite hook that combines all store slices
* 3. Re-exports all individual stores for direct access
* 2. Re-exports all individual stores for direct access
*/
// === Re-export Individual Stores ===
@@ -26,6 +25,12 @@ export type { WorkflowStore, WorkflowStateSlice, WorkflowActionsSlice, Workflow,
export { useConfigStore, setConfigStoreClient } from './configStore';
export type { ConfigStore, ConfigStateSlice, ConfigActionsSlice, QuickConfig, WorkspaceInfo, ChannelInfo, ScheduledTask, SkillInfo } from './configStore';
export { useSecurityStore, setSecurityStoreClient } from './securityStore';
export type { SecurityStore, SecurityStateSlice, SecurityActionsSlice, SecurityLayer, SecurityStatus, AuditLogEntry } from './securityStore';
export { useSessionStore, setSessionStoreClient } from './sessionStore';
export type { SessionStore, SessionStateSlice, SessionActionsSlice, Session, SessionMessage } from './sessionStore';
// === New Stores ===
export { useMemoryGraphStore } from './memoryGraphStore';
export type { MemoryGraphStore, GraphNode, GraphEdge, GraphFilter, GraphLayout } from './memoryGraphStore';
@@ -49,14 +54,15 @@ export type {
SessionOptions,
} from '../components/BrowserHand/templates/types';
// === Composite Store Hook ===
// === Store Initialization ===
import { useMemo } from 'react';
import { useConnectionStore, getClient } from './connectionStore';
import { useAgentStore, setAgentStoreClient } from './agentStore';
import { useHandStore, setHandStoreClient } from './handStore';
import { useWorkflowStore, setWorkflowStoreClient } from './workflowStore';
import { useConfigStore, setConfigStoreClient } from './configStore';
import { getClient } from './connectionStore';
import { setAgentStoreClient } from './agentStore';
import { setHandStoreClient } from './handStore';
import { setWorkflowStoreClient } from './workflowStore';
import { setConfigStoreClient } from './configStore';
import { setSecurityStoreClient } from './securityStore';
import { setSessionStoreClient } from './sessionStore';
/**
* Initialize all stores with the shared client.
@@ -70,207 +76,8 @@ export function initializeStores(): void {
setHandStoreClient(client);
setWorkflowStoreClient(client);
setConfigStoreClient(client);
}
/**
* Hook that provides a composite view of all stores.
* Use this for components that need access to multiple store slices.
*
* For components that only need specific slices, import the individual
* store hooks directly (e.g., useConnectionStore, useAgentStore).
*/
export function useCompositeStore() {
// Subscribe to all stores
const connectionState = useConnectionStore((s) => s.connectionState);
const gatewayVersion = useConnectionStore((s) => s.gatewayVersion);
const connectionError = useConnectionStore((s) => s.error);
const logs = useConnectionStore((s) => s.logs);
const localGateway = useConnectionStore((s) => s.localGateway);
const localGatewayBusy = useConnectionStore((s) => s.localGatewayBusy);
const isLoading = useConnectionStore((s) => s.isLoading);
const client = useConnectionStore((s) => s.client);
const clones = useAgentStore((s) => s.clones);
const usageStats = useAgentStore((s) => s.usageStats);
const pluginStatus = useAgentStore((s) => s.pluginStatus);
const hands = useHandStore((s) => s.hands);
const handRuns = useHandStore((s) => s.handRuns);
const triggers = useHandStore((s) => s.triggers);
const approvals = useHandStore((s) => s.approvals);
const workflows = useWorkflowStore((s) => s.workflows);
const workflowRuns = useWorkflowStore((s) => s.workflowRuns);
const quickConfig = useConfigStore((s) => s.quickConfig);
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
const channels = useConfigStore((s) => s.channels);
const scheduledTasks = useConfigStore((s) => s.scheduledTasks);
const skillsCatalog = useConfigStore((s) => s.skillsCatalog);
const models = useConfigStore((s) => s.models);
const modelsLoading = useConfigStore((s) => s.modelsLoading);
const modelsError = useConfigStore((s) => s.modelsError);
// Get all actions
const connect = useConnectionStore((s) => s.connect);
const disconnect = useConnectionStore((s) => s.disconnect);
const clearLogs = useConnectionStore((s) => s.clearLogs);
const refreshLocalGateway = useConnectionStore((s) => s.refreshLocalGateway);
const startLocalGateway = useConnectionStore((s) => s.startLocalGateway);
const stopLocalGateway = useConnectionStore((s) => s.stopLocalGateway);
const restartLocalGateway = useConnectionStore((s) => s.restartLocalGateway);
const loadClones = useAgentStore((s) => s.loadClones);
const createClone = useAgentStore((s) => s.createClone);
const updateClone = useAgentStore((s) => s.updateClone);
const deleteClone = useAgentStore((s) => s.deleteClone);
const loadUsageStats = useAgentStore((s) => s.loadUsageStats);
const loadPluginStatus = useAgentStore((s) => s.loadPluginStatus);
const loadHands = useHandStore((s) => s.loadHands);
const getHandDetails = useHandStore((s) => s.getHandDetails);
const triggerHand = useHandStore((s) => s.triggerHand);
const loadHandRuns = useHandStore((s) => s.loadHandRuns);
const loadTriggers = useHandStore((s) => s.loadTriggers);
const createTrigger = useHandStore((s) => s.createTrigger);
const deleteTrigger = useHandStore((s) => s.deleteTrigger);
const loadApprovals = useHandStore((s) => s.loadApprovals);
const respondToApproval = useHandStore((s) => s.respondToApproval);
const loadWorkflows = useWorkflowStore((s) => s.loadWorkflows);
const getWorkflow = useWorkflowStore((s) => s.getWorkflow);
const createWorkflow = useWorkflowStore((s) => s.createWorkflow);
const updateWorkflow = useWorkflowStore((s) => s.updateWorkflow);
const deleteWorkflow = useWorkflowStore((s) => s.deleteWorkflow);
const triggerWorkflow = useWorkflowStore((s) => s.triggerWorkflow);
const loadWorkflowRuns = useWorkflowStore((s) => s.loadWorkflowRuns);
const loadQuickConfig = useConfigStore((s) => s.loadQuickConfig);
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
const loadChannels = useConfigStore((s) => s.loadChannels);
const getChannel = useConfigStore((s) => s.getChannel);
const createChannel = useConfigStore((s) => s.createChannel);
const updateChannel = useConfigStore((s) => s.updateChannel);
const deleteChannel = useConfigStore((s) => s.deleteChannel);
const loadScheduledTasks = useConfigStore((s) => s.loadScheduledTasks);
const createScheduledTask = useConfigStore((s) => s.createScheduledTask);
const loadSkillsCatalog = useConfigStore((s) => s.loadSkillsCatalog);
const getSkill = useConfigStore((s) => s.getSkill);
const createSkill = useConfigStore((s) => s.createSkill);
const updateSkill = useConfigStore((s) => s.updateSkill);
const deleteSkill = useConfigStore((s) => s.deleteSkill);
const loadModels = useConfigStore((s) => s.loadModels);
// Memoize the composite store to prevent unnecessary re-renders
return useMemo(() => ({
// Connection state
connectionState,
gatewayVersion,
error: connectionError,
logs,
localGateway,
localGatewayBusy,
isLoading,
client,
// Agent state
clones,
usageStats,
pluginStatus,
// Hand state
hands,
handRuns,
triggers,
approvals,
// Workflow state
workflows,
workflowRuns,
// Config state
quickConfig,
workspaceInfo,
channels,
scheduledTasks,
skillsCatalog,
models,
modelsLoading,
modelsError,
// Connection actions
connect,
disconnect,
clearLogs,
refreshLocalGateway,
startLocalGateway,
stopLocalGateway,
restartLocalGateway,
// Agent actions
loadClones,
createClone,
updateClone,
deleteClone,
loadUsageStats,
loadPluginStatus,
// Hand actions
loadHands,
getHandDetails,
triggerHand,
loadHandRuns,
loadTriggers,
createTrigger,
deleteTrigger,
loadApprovals,
respondToApproval,
// Workflow actions
loadWorkflows,
getWorkflow,
createWorkflow,
updateWorkflow,
deleteWorkflow,
triggerWorkflow,
loadWorkflowRuns,
// Config actions
loadQuickConfig,
saveQuickConfig,
loadWorkspaceInfo,
loadChannels,
getChannel,
createChannel,
updateChannel,
deleteChannel,
loadScheduledTasks,
createScheduledTask,
loadSkillsCatalog,
getSkill,
createSkill,
updateSkill,
deleteSkill,
loadModels,
// Legacy sendMessage (delegates to client)
sendMessage: async (message: string, sessionKey?: string) => {
return client.chat(message, { sessionKey });
},
}), [
connectionState, gatewayVersion, connectionError, logs, localGateway, localGatewayBusy, isLoading, client,
clones, usageStats, pluginStatus,
hands, handRuns, triggers, approvals,
workflows, workflowRuns,
quickConfig, workspaceInfo, channels, scheduledTasks, skillsCatalog, models, modelsLoading, modelsError,
connect, disconnect, clearLogs, refreshLocalGateway, startLocalGateway, stopLocalGateway, restartLocalGateway,
loadClones, createClone, updateClone, deleteClone, loadUsageStats, loadPluginStatus,
loadHands, getHandDetails, triggerHand, loadHandRuns, loadTriggers, createTrigger, deleteTrigger, loadApprovals, respondToApproval,
loadWorkflows, getWorkflow, createWorkflow, updateWorkflow, deleteWorkflow, triggerWorkflow, loadWorkflowRuns,
loadQuickConfig, saveQuickConfig, loadWorkspaceInfo, loadChannels, getChannel, createChannel, updateChannel, deleteChannel,
loadScheduledTasks, createScheduledTask, loadSkillsCatalog, getSkill, createSkill, updateSkill, deleteSkill, loadModels,
]);
setSecurityStoreClient(client);
setSessionStoreClient(client);
}
/**