feat: Implement Worker Service for long-running HTTP service with PM2 management
- Introduced WorkerService class to handle HTTP requests and manage sessions. - Added endpoints for health check, session management, and data retrieval. - Integrated ChromaSync for background data synchronization. - Implemented SSE for real-time updates to connected clients. - Added error handling and logging throughout the service. - Cached Claude executable path for improved performance. - Included settings management for user configuration. - Established database interactions for session and observation management.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Observation } from '../types';
|
||||
import { formatDate } from '../utils/formatters';
|
||||
|
||||
@@ -7,19 +7,122 @@ interface ObservationCardProps {
|
||||
}
|
||||
|
||||
export function ObservationCard({ observation }: ObservationCardProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const date = formatDate(observation.created_at_epoch);
|
||||
|
||||
// Parse JSON fields
|
||||
const facts = observation.facts ? JSON.parse(observation.facts) : [];
|
||||
const concepts = observation.concepts ? JSON.parse(observation.concepts) : [];
|
||||
const filesRead = observation.files_read ? JSON.parse(observation.files_read) : [];
|
||||
const filesModified = observation.files_modified ? JSON.parse(observation.files_modified) : [];
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className={`card ${isExpanded ? 'card-expanded' : ''}`}>
|
||||
{/* Header - always visible */}
|
||||
<div className="card-header">
|
||||
<span className="card-type">{observation.type}</span>
|
||||
<span>{observation.project}</span>
|
||||
<span className={`card-type type-${observation.type}`}>
|
||||
{observation.type}
|
||||
</span>
|
||||
<span className="card-project">{observation.project}</span>
|
||||
</div>
|
||||
|
||||
{/* Title/Subtitle - always visible */}
|
||||
<div className="card-title">{observation.title || 'Untitled'}</div>
|
||||
{observation.subtitle && (
|
||||
<div className="card-subtitle">{observation.subtitle}</div>
|
||||
)}
|
||||
<div className="card-meta">#{observation.id} • {date}</div>
|
||||
|
||||
{/* Metadata + Expand button - always visible */}
|
||||
<div className="card-meta">
|
||||
<span>#{observation.id} • {date}</span>
|
||||
<button
|
||||
className="expand-toggle"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
{isExpanded ? '▲ Less' : '▼ More'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Expanded content - conditional */}
|
||||
{isExpanded && (
|
||||
<div className="card-expanded-content">
|
||||
|
||||
{/* Narrative Section */}
|
||||
{observation.narrative && (
|
||||
<div className="card-section">
|
||||
<div className="section-header">📝 Narrative</div>
|
||||
<div className="section-content narrative">
|
||||
{observation.narrative}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Facts Section */}
|
||||
{facts.length > 0 && (
|
||||
<div className="card-section">
|
||||
<div className="section-header">📌 Key Facts</div>
|
||||
<ul className="section-content facts-list">
|
||||
{facts.map((fact: string, i: number) => (
|
||||
<li key={i}>{fact}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Concepts Section */}
|
||||
{concepts.length > 0 && (
|
||||
<div className="card-section">
|
||||
<div className="section-header">🏷️ Concepts</div>
|
||||
<div className="section-content concepts">
|
||||
{concepts.map((concept: string, i: number) => (
|
||||
<span key={i} className="concept-tag">{concept}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Files Section */}
|
||||
{(filesRead.length > 0 || filesModified.length > 0) && (
|
||||
<div className="card-section">
|
||||
<div className="section-header">📁 Files</div>
|
||||
<div className="section-content files">
|
||||
{filesRead.length > 0 && (
|
||||
<div className="file-group">
|
||||
<div className="file-group-label">📖 Read:</div>
|
||||
{filesRead.map((file: string, i: number) => (
|
||||
<div key={i} className="file-path">{file}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{filesModified.length > 0 && (
|
||||
<div className="file-group">
|
||||
<div className="file-group-label">✏️ Modified:</div>
|
||||
{filesModified.map((file: string, i: number) => (
|
||||
<div key={i} className="file-path">{file}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Session Info Section */}
|
||||
<div className="card-section">
|
||||
<div className="section-header">🔗 Session Info</div>
|
||||
<div className="section-content session-info">
|
||||
{observation.prompt_number && (
|
||||
<span>Prompt #{observation.prompt_number}</span>
|
||||
)}
|
||||
{observation.sdk_session_id && (
|
||||
<span className="session-id">
|
||||
Session: {observation.sdk_session_id.substring(0, 8)}...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ function usePaginationFor(endpoint: string, dataType: DataType, currentFilter: s
|
||||
}));
|
||||
|
||||
setOffset(prev => prev + UI.PAGINATION_PAGE_SIZE);
|
||||
return data[dataType] as DataItem[];
|
||||
return data.items as DataItem[];
|
||||
} catch (error) {
|
||||
console.error(`Failed to load ${dataType}:`, error);
|
||||
setState(prev => ({ ...prev, isLoading: false }));
|
||||
|
||||
@@ -13,14 +13,6 @@ export function useSSE() {
|
||||
const eventSourceRef = useRef<EventSource | null>(null);
|
||||
const reconnectTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
// Fetch initial processing status on mount
|
||||
useEffect(() => {
|
||||
fetch(API_ENDPOINTS.PROCESSING_STATUS)
|
||||
.then(res => res.json())
|
||||
.then(data => setIsProcessing(data.isProcessing))
|
||||
.catch(err => console.error('[SSE] Failed to fetch initial processing status:', err));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const connect = () => {
|
||||
// Clean up existing connection
|
||||
|
||||
+11
-4
@@ -1,11 +1,18 @@
|
||||
export interface Observation {
|
||||
id: number;
|
||||
session_id: string;
|
||||
sdk_session_id: string;
|
||||
project: string;
|
||||
type: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
content?: string;
|
||||
title: string | null;
|
||||
subtitle: string | null;
|
||||
narrative: string | null;
|
||||
text: string | null;
|
||||
facts: string | null;
|
||||
concepts: string | null;
|
||||
files_read: string | null;
|
||||
files_modified: string | null;
|
||||
prompt_number: number | null;
|
||||
created_at: string;
|
||||
created_at_epoch: number;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user