feat: OpenClaw WebSocket integration with workspace file preview
- Migrate OpenClaw from HTTP (port 8004) to WebSocket (port 18789) - Add workspace file list and content preview handlers - Add OpenClawStatus component with agent/skills view - Add OpenClawView panel in trader interface - Add Zustand store for OpenClaw state management - Fix gateway logging noise (yfinance, websockets) - Fix RunWorkspaceManager.get_agent_asset_dir attribute error - Handle missing workspace files gracefully in preview Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
227
frontend/src/store/openclawStore.js
Normal file
227
frontend/src/store/openclawStore.js
Normal file
@@ -0,0 +1,227 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
export const useOpenClawStore = create(
|
||||
persist(
|
||||
(set) => ({
|
||||
// Raw data
|
||||
openclawStatus: null,
|
||||
openclawSessions: [],
|
||||
openclawSessionDetail: null,
|
||||
openclawSessionHistory: [],
|
||||
openclawCronJobs: [],
|
||||
openclawApprovals: [],
|
||||
|
||||
// Loading states
|
||||
isStatusLoading: false,
|
||||
isSessionsLoading: false,
|
||||
isSessionDetailLoading: false,
|
||||
isCronLoading: false,
|
||||
isApprovalsLoading: false,
|
||||
|
||||
// Error states
|
||||
statusError: null,
|
||||
sessionsError: null,
|
||||
sessionDetailError: null,
|
||||
cronError: null,
|
||||
approvalsError: null,
|
||||
|
||||
// Agents state
|
||||
agents: [],
|
||||
agentsLoading: false,
|
||||
agentsError: null,
|
||||
agentsPresence: {},
|
||||
|
||||
// Skills state
|
||||
skills: [],
|
||||
skillsLoading: false,
|
||||
skillsError: null,
|
||||
|
||||
// Models state
|
||||
models: [],
|
||||
modelsLoading: false,
|
||||
modelsError: null,
|
||||
|
||||
// Hooks state
|
||||
hooks: [],
|
||||
hooksLoading: false,
|
||||
hooksError: null,
|
||||
|
||||
// Plugins state
|
||||
plugins: [],
|
||||
pluginsLoading: false,
|
||||
pluginsError: null,
|
||||
|
||||
// Secrets audit state
|
||||
secretsAudit: null,
|
||||
secretsAuditLoading: false,
|
||||
secretsAuditError: null,
|
||||
|
||||
// Security audit state
|
||||
securityAudit: null,
|
||||
securityAuditLoading: false,
|
||||
securityAuditError: null,
|
||||
|
||||
// Daemon status state
|
||||
daemonStatus: null,
|
||||
daemonStatusLoading: false,
|
||||
daemonStatusError: null,
|
||||
|
||||
// Pairing state
|
||||
pairing: null,
|
||||
pairingLoading: false,
|
||||
pairingError: null,
|
||||
|
||||
// QR code state
|
||||
qrCode: null,
|
||||
qrCodeLoading: false,
|
||||
qrCodeError: null,
|
||||
|
||||
// Update status state
|
||||
updateStatus: null,
|
||||
updateStatusLoading: false,
|
||||
updateStatusError: null,
|
||||
|
||||
// Models aliases state
|
||||
modelsAliases: null,
|
||||
modelsAliasesLoading: false,
|
||||
modelsAliasesError: null,
|
||||
|
||||
// Models fallbacks state
|
||||
modelsFallbacks: [],
|
||||
modelsFallbacksLoading: false,
|
||||
modelsFallbacksError: null,
|
||||
|
||||
// Models image fallbacks state
|
||||
modelsImageFallbacks: [],
|
||||
modelsImageFallbacksLoading: false,
|
||||
modelsImageFallbacksError: null,
|
||||
|
||||
// Skill update state
|
||||
skillUpdate: null,
|
||||
skillUpdateLoading: false,
|
||||
skillUpdateError: null,
|
||||
|
||||
// Workspace files state (per agent, keyed by workspace path)
|
||||
workspaceFiles: {},
|
||||
workspaceFilesLoading: false,
|
||||
workspaceFilesError: null,
|
||||
|
||||
// Workspace file content (keyed by "agentId:filename")
|
||||
workspaceFileContent: {},
|
||||
|
||||
// Selected session key for detail/history drill-down
|
||||
selectedSessionKey: null,
|
||||
|
||||
// WebSocket client ref (set by App.jsx on connection)
|
||||
clientRef: null,
|
||||
setClientRef: (ref) => set({ clientRef: ref }),
|
||||
|
||||
// Setters
|
||||
setOpenclawStatus: (data) => set({ openclawStatus: data, statusError: null }),
|
||||
setOpenclawSessions: (data) => set({ openclawSessions: data?.sessions || [], sessionsError: null }),
|
||||
setOpenclawSessionDetail: (data) => set({ openclawSessionDetail: data?.session || null, sessionDetailError: null }),
|
||||
setOpenclawSessionHistory: (data) => set({ openclawSessionHistory: data?.history || [], sessionDetailError: null }),
|
||||
setOpenclawCronJobs: (data) => set({ openclawCronJobs: data?.cron || [], cronError: null }),
|
||||
setOpenclawApprovals: (data) => set({ openclawApprovals: data?.approvals || [], approvalsError: null }),
|
||||
|
||||
setSelectedSessionKey: (key) => set({ selectedSessionKey: key }),
|
||||
|
||||
setStatusLoading: (v) => set({ isStatusLoading: v }),
|
||||
setSessionsLoading: (v) => set({ isSessionsLoading: v }),
|
||||
setSessionDetailLoading: (v) => set({ isSessionDetailLoading: v }),
|
||||
setCronLoading: (v) => set({ isCronLoading: v }),
|
||||
setApprovalsLoading: (v) => set({ isApprovalsLoading: v }),
|
||||
|
||||
setStatusError: (e) => set({ statusError: e }),
|
||||
setSessionsError: (e) => set({ sessionsError: e }),
|
||||
setSessionDetailError: (e) => set({ sessionDetailError: e }),
|
||||
setCronError: (e) => set({ cronError: e }),
|
||||
setApprovalsError: (e) => set({ approvalsError: e }),
|
||||
|
||||
setAgents: (agents) => set({ agents }),
|
||||
setAgentsLoading: (loading) => set({ agentsLoading: loading }),
|
||||
setAgentsError: (error) => set({ agentsError: error }),
|
||||
setAgentsPresence: (presence) => set({ agentsPresence: presence }),
|
||||
setSkills: (skills) => set({ skills }),
|
||||
setSkillsLoading: (loading) => set({ skillsLoading: loading }),
|
||||
setSkillsError: (error) => set({ skillsError: error }),
|
||||
setModels: (models) => set({ models }),
|
||||
setModelsLoading: (loading) => set({ modelsLoading: loading }),
|
||||
setModelsError: (error) => set({ modelsError: error }),
|
||||
|
||||
setHooks: (hooks) => set({ hooks }),
|
||||
setHooksLoading: (loading) => set({ hooksLoading: loading }),
|
||||
setHooksError: (error) => set({ hooksError: error }),
|
||||
setPlugins: (plugins) => set({ plugins }),
|
||||
setPluginsLoading: (loading) => set({ pluginsLoading: loading }),
|
||||
setPluginsError: (error) => set({ pluginsError: error }),
|
||||
setSecretsAudit: (data) => set({ secretsAudit: data }),
|
||||
setSecretsAuditLoading: (loading) => set({ secretsAuditLoading: loading }),
|
||||
setSecretsAuditError: (error) => set({ secretsAuditError: error }),
|
||||
setSecurityAudit: (data) => set({ securityAudit: data }),
|
||||
setSecurityAuditLoading: (loading) => set({ securityAuditLoading: loading }),
|
||||
setSecurityAuditError: (error) => set({ securityAuditError: error }),
|
||||
setDaemonStatus: (data) => set({ daemonStatus: data }),
|
||||
setDaemonStatusLoading: (loading) => set({ daemonStatusLoading: loading }),
|
||||
setDaemonStatusError: (error) => set({ daemonStatusError: error }),
|
||||
setPairing: (data) => set({ pairing: data }),
|
||||
setPairingLoading: (loading) => set({ pairingLoading: loading }),
|
||||
setPairingError: (error) => set({ pairingError: error }),
|
||||
setQrCode: (data) => set({ qrCode: data }),
|
||||
setQrCodeLoading: (loading) => set({ qrCodeLoading: loading }),
|
||||
setQrCodeError: (error) => set({ qrCodeError: error }),
|
||||
setUpdateStatus: (data) => set({ updateStatus: data }),
|
||||
setUpdateStatusLoading: (loading) => set({ updateStatusLoading: loading }),
|
||||
setUpdateStatusError: (error) => set({ updateStatusError: error }),
|
||||
setModelsAliases: (data) => set({ modelsAliases: data }),
|
||||
setModelsAliasesLoading: (loading) => set({ modelsAliasesLoading: loading }),
|
||||
setModelsAliasesError: (error) => set({ modelsAliasesError: error }),
|
||||
setModelsFallbacks: (data) => set({ modelsFallbacks: data }),
|
||||
setModelsFallbacksLoading: (loading) => set({ modelsFallbacksLoading: loading }),
|
||||
setModelsFallbacksError: (error) => set({ modelsFallbacksError: error }),
|
||||
setModelsImageFallbacks: (data) => set({ modelsImageFallbacks: data }),
|
||||
setModelsImageFallbacksLoading: (loading) => set({ modelsImageFallbacksLoading: loading }),
|
||||
setModelsImageFallbacksError: (error) => set({ modelsImageFallbacksError: error }),
|
||||
setSkillUpdate: (data) => set({ skillUpdate: data }),
|
||||
setSkillUpdateLoading: (loading) => set({ skillUpdateLoading: loading }),
|
||||
setSkillUpdateError: (error) => set({ skillUpdateError: error }),
|
||||
|
||||
setWorkspaceFiles: (workspace, data) => set((state) => ({
|
||||
workspaceFiles: { ...state.workspaceFiles, [workspace]: data },
|
||||
})),
|
||||
setWorkspaceFilesLoading: (loading) => set({ workspaceFilesLoading: loading }),
|
||||
setWorkspaceFilesError: (error) => set({ workspaceFilesError: error }),
|
||||
setWorkspaceFileContent: (key, content) => set((state) => ({
|
||||
workspaceFileContent: { ...state.workspaceFileContent, [key]: content },
|
||||
})),
|
||||
}),
|
||||
{
|
||||
name: "openclaw-store",
|
||||
// Skip persisting ephemeral UI state
|
||||
partialize: (state) => ({
|
||||
// Persist only data, not loading/error/UI states
|
||||
openclawStatus: state.openclawStatus,
|
||||
openclawSessions: state.openclawSessions,
|
||||
openclawCronJobs: state.openclawCronJobs,
|
||||
openclawApprovals: state.openclawApprovals,
|
||||
agents: state.agents,
|
||||
agentsPresence: state.agentsPresence,
|
||||
skills: state.skills,
|
||||
models: state.models,
|
||||
hooks: state.hooks,
|
||||
plugins: state.plugins,
|
||||
secretsAudit: state.secretsAudit,
|
||||
securityAudit: state.securityAudit,
|
||||
daemonStatus: state.daemonStatus,
|
||||
pairing: state.pairing,
|
||||
qrCode: state.qrCode,
|
||||
updateStatus: state.updateStatus,
|
||||
modelsAliases: state.modelsAliases,
|
||||
modelsFallbacks: state.modelsFallbacks,
|
||||
modelsImageFallbacks: state.modelsImageFallbacks,
|
||||
skillUpdate: state.skillUpdate,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user