vibemon 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,168 @@
1
+ /**
2
+ * State and timer management for Vibe Monitor (Multi-Window Architecture)
3
+ *
4
+ * Each window manages its own state. StateManager handles:
5
+ * - Per-project timers (state timeouts, window close timeouts)
6
+ * - State data validation
7
+ */
8
+
9
+ const {
10
+ IDLE_TIMEOUT, SLEEP_TIMEOUT, WINDOW_CLOSE_TIMEOUT,
11
+ CHARACTER_CONFIG, DEFAULT_CHARACTER
12
+ } = require('../shared/config.cjs');
13
+
14
+ class StateManager {
15
+ constructor() {
16
+ // Per-project timers
17
+ this.stateTimeoutTimers = new Map();
18
+ this.windowCloseTimers = new Map();
19
+
20
+ // Callbacks (set by main.js)
21
+ this.onStateTimeout = null; // (projectId, newState) => void
22
+ this.onWindowCloseTimeout = null; // (projectId) => void
23
+ }
24
+
25
+ // Timer management - per project
26
+
27
+ /**
28
+ * Clear state timeout timer for a specific project
29
+ * @param {string} projectId - Project identifier
30
+ */
31
+ clearStateTimeout(projectId) {
32
+ const timer = this.stateTimeoutTimers.get(projectId);
33
+ if (timer) {
34
+ clearTimeout(timer);
35
+ this.stateTimeoutTimers.delete(projectId);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Clear window close timer for a specific project
41
+ * @param {string} projectId - Project identifier
42
+ */
43
+ clearWindowCloseTimer(projectId) {
44
+ const timer = this.windowCloseTimers.get(projectId);
45
+ if (timer) {
46
+ clearTimeout(timer);
47
+ this.windowCloseTimers.delete(projectId);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Set up window close timer for a project in sleep state
53
+ * @param {string} projectId - Project identifier
54
+ * @param {string} currentState - Current state of the project
55
+ */
56
+ setupWindowCloseTimer(projectId, currentState) {
57
+ this.clearWindowCloseTimer(projectId);
58
+
59
+ if (currentState === 'sleep' && this.onWindowCloseTimeout) {
60
+ const timer = setTimeout(() => {
61
+ this.windowCloseTimers.delete(projectId);
62
+ this.onWindowCloseTimeout(projectId);
63
+ }, WINDOW_CLOSE_TIMEOUT);
64
+ this.windowCloseTimers.set(projectId, timer);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Set up state timeout for automatic state transitions
70
+ * @param {string} projectId - Project identifier
71
+ * @param {string} currentState - Current state of the project
72
+ */
73
+ setupStateTimeout(projectId, currentState) {
74
+ this.clearStateTimeout(projectId);
75
+ this.clearWindowCloseTimer(projectId);
76
+
77
+ if (currentState === 'start' || currentState === 'done') {
78
+ // start/done -> idle after 1 minute
79
+ const timer = setTimeout(() => {
80
+ this.stateTimeoutTimers.delete(projectId);
81
+ if (this.onStateTimeout) {
82
+ this.onStateTimeout(projectId, 'idle');
83
+ }
84
+ }, IDLE_TIMEOUT);
85
+ this.stateTimeoutTimers.set(projectId, timer);
86
+ } else if (currentState === 'planning' || currentState === 'thinking' ||
87
+ currentState === 'working' || currentState === 'notification') {
88
+ // planning/thinking/working/notification -> idle after 5 minutes
89
+ const timer = setTimeout(() => {
90
+ this.stateTimeoutTimers.delete(projectId);
91
+ if (this.onStateTimeout) {
92
+ this.onStateTimeout(projectId, 'idle');
93
+ }
94
+ }, SLEEP_TIMEOUT);
95
+ this.stateTimeoutTimers.set(projectId, timer);
96
+ } else if (currentState === 'idle') {
97
+ // idle -> sleep after 5 minutes
98
+ const timer = setTimeout(() => {
99
+ this.stateTimeoutTimers.delete(projectId);
100
+ if (this.onStateTimeout) {
101
+ this.onStateTimeout(projectId, 'sleep');
102
+ }
103
+ }, SLEEP_TIMEOUT);
104
+ this.stateTimeoutTimers.set(projectId, timer);
105
+ } else if (currentState === 'sleep') {
106
+ // sleep -> close window after 10 minutes
107
+ this.setupWindowCloseTimer(projectId, currentState);
108
+ }
109
+ }
110
+
111
+ // Validation
112
+
113
+ /**
114
+ * Validate and normalize state data
115
+ * @param {Object} data - Incoming state data
116
+ * @returns {Object} Normalized data with validated character field
117
+ */
118
+ validateStateData(data) {
119
+ if (!data || typeof data !== 'object') {
120
+ return { valid: false, error: 'Invalid data format' };
121
+ }
122
+
123
+ // Create a new normalized data object (immutability)
124
+ const normalized = { ...data };
125
+
126
+ // Validate and normalize character field
127
+ if (normalized.character !== undefined) {
128
+ normalized.character = CHARACTER_CONFIG[normalized.character]
129
+ ? normalized.character
130
+ : DEFAULT_CHARACTER;
131
+ }
132
+
133
+ return {
134
+ valid: true,
135
+ data: normalized
136
+ };
137
+ }
138
+
139
+ // Cleanup
140
+
141
+ /**
142
+ * Clear all timers for a specific project
143
+ * @param {string} projectId - Project identifier
144
+ */
145
+ cleanupProject(projectId) {
146
+ this.clearStateTimeout(projectId);
147
+ this.clearWindowCloseTimer(projectId);
148
+ }
149
+
150
+ /**
151
+ * Clear all timers for all projects
152
+ */
153
+ cleanup() {
154
+ // Clear all state timeout timers
155
+ for (const [, timer] of this.stateTimeoutTimers) {
156
+ clearTimeout(timer);
157
+ }
158
+ this.stateTimeoutTimers.clear();
159
+
160
+ // Clear all window close timers
161
+ for (const [, timer] of this.windowCloseTimers) {
162
+ clearTimeout(timer);
163
+ }
164
+ this.windowCloseTimers.clear();
165
+ }
166
+ }
167
+
168
+ module.exports = { StateManager };