everydaytok commited on
Commit
8e22657
·
verified ·
1 Parent(s): a742b01

Update stateManager.js

Browse files
Files changed (1) hide show
  1. stateManager.js +95 -170
stateManager.js CHANGED
@@ -1,186 +1,111 @@
1
  import { createClient } from '@supabase/supabase-js';
2
 
3
- // --- SUPABASE SETUP ---
4
  let supabase = null;
 
5
 
6
  export const initDB = () => {
7
- if (process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE_KEY) {
8
- supabase = createClient(
9
- process.env.SUPABASE_URL,
10
- process.env.SUPABASE_SERVICE_ROLE_KEY
11
- );
12
- console.log("⚡ Supabase Client Initialized");
13
- } else {
14
- console.warn("⚠️ Supabase Credentials Missing - Memory Mode Only");
15
- }
16
  };
17
 
18
- const activeProjects = new Map();
19
-
20
  export const StateManager = {
21
- getProject: async (projectId) => {
22
- // 1. Try Memory
23
- if (activeProjects.has(projectId)) {
24
- return activeProjects.get(projectId);
25
- }
26
-
27
- // 2. Try Supabase
28
- if (supabase) {
29
- try {
30
- const { data, error } = await supabase
31
- .from('projects')
32
- .select('*')
33
- .eq('id', projectId)
34
- .single();
35
-
36
- if (data) {
37
- // Flatten DB structure (info + state) to Memory Object
38
- const memoryObject = {
39
- ...data.info, // Title, stats, description, status
40
- ...data.state, // workerHistory, pmHistory, queue
41
- id: data.id,
42
- userId: data.user_id,
43
- thumbnail: data.thumbnail,
44
- lastActive: Date.now(),
45
- lastUpdated: Date.now()
46
- };
47
-
48
- // Ensure arrays exist
49
- if (!memoryObject.commandQueue) memoryObject.commandQueue = [];
50
- if (!memoryObject.workerHistory) memoryObject.workerHistory = [];
51
- if (!memoryObject.pmHistory) memoryObject.pmHistory = [];
52
- if (!memoryObject.failureCount) memoryObject.failureCount = 0;
53
-
54
- activeProjects.set(projectId, memoryObject);
55
- return memoryObject;
56
- }
57
- } catch (err) {
58
- console.error(`[StateManager] Error reading DB for ${projectId}:`, err);
59
- }
60
- }
61
-
62
- return null;
63
- },
64
-
65
- updateProject: async (projectId, data) => {
66
- let current = activeProjects.get(projectId);
67
-
68
- // Safety check: ensure we have base state
69
- if (!current) {
70
- current = await StateManager.getProject(projectId) || {
71
- commandQueue: [], workerHistory: [], pmHistory: [], failureCount: 0
72
- };
73
- }
74
-
75
- // 1. Update Memory (Flat Merge)
76
- const newData = {
77
- ...current,
78
- ...data,
79
- lastActive: Date.now(),
80
- lastUpdated: Date.now()
81
- };
82
- activeProjects.set(projectId, newData);
83
-
84
- // 2. Update Supabase
85
- if (supabase) {
86
- // We separate data into columns: 'info' and 'state'
87
- const infoUpdate = {
88
- title: newData.title,
89
- stats: newData.stats,
90
- description: newData.description,
91
- status: newData.status,
92
- lastUpdated: newData.lastUpdated
93
- };
94
-
95
- const stateUpdate = {
96
- workerHistory: newData.workerHistory,
97
- pmHistory: newData.pmHistory,
98
- commandQueue: newData.commandQueue,
99
- failureCount: newData.failureCount,
100
- gdd: newData.gdd
101
- };
102
-
103
- const updatePayload = {
104
- info: infoUpdate,
105
- state: stateUpdate,
106
- };
107
-
108
- if (data.thumbnail) updatePayload.thumbnail = data.thumbnail;
109
-
110
- await supabase
111
- .from('projects')
112
- .update(updatePayload)
113
- .eq('id', projectId);
114
- }
115
-
116
- return newData;
117
- },
118
-
119
- queueCommand: async (projectId, input) => {
120
- const project = await StateManager.getProject(projectId);
121
- if (!project) return;
122
-
123
- let command = null;
124
-
125
- if (typeof input === 'object' && input.type && input.payload) {
126
- command = input;
127
- }
128
- else if (typeof input === 'string') {
129
- const rawResponse = input;
130
 
131
- // Filters
132
- if (rawResponse.includes("[ASK_PM:")) return;
133
- if (rawResponse.includes("[ROUTE_TO_PM:")) return;
134
- if (rawResponse.includes("[GENERATE_IMAGE:") && !rawResponse.includes("```")) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- // Extractors
137
- const codeMatch = rawResponse.match(/```(?:lua|luau)?([\s\S]*?)```/i);
138
- const readScriptMatch = rawResponse.match(/\[READ_SCRIPT:\s*(.*?)\]/);
139
- const readHierarchyMatch = rawResponse.match(/\[READ_HIERARCHY:\s*(.*?)\]/);
140
- const readLogsMatch = rawResponse.includes("[READ_LOGS]");
141
 
142
- if (codeMatch) command = { type: "EXECUTE", payload: codeMatch[1].trim() };
143
- else if (readScriptMatch) command = { type: "READ_SCRIPT", payload: readScriptMatch[1].trim() };
144
- else if (readHierarchyMatch) command = { type: "READ_HIERARCHY", payload: readHierarchyMatch[1].trim() };
145
- else if (readLogsMatch) command = { type: "READ_LOGS", payload: null };
146
- }
147
 
148
- if (command) {
149
- project.commandQueue.push(command);
150
- console.log(`[${projectId}] Queued Action: ${command.type}`);
151
- await StateManager.updateProject(projectId, { commandQueue: project.commandQueue });
152
- }
153
- },
154
-
155
- popCommand: async (projectId) => {
156
- const project = await StateManager.getProject(projectId);
157
- if (!project || !project.commandQueue.length) return null;
158
-
159
- const command = project.commandQueue.shift();
160
- await StateManager.updateProject(projectId, { commandQueue: project.commandQueue });
161
-
162
- return command;
163
- },
164
-
165
- cleanupMemory: () => {
166
- const now = Date.now();
167
- const FOUR_HOURS = 4 * 60 * 60 * 1000;
168
- let count = 0;
169
-
170
- for (const [id, data] of activeProjects.entries()) {
171
- if (!data.lastActive) {
172
- data.lastActive = now;
173
- continue;
174
  }
175
-
176
- if (now - data.lastActive > FOUR_HOURS) {
177
- activeProjects.delete(id);
178
- count++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
- }
181
- return count;
182
- },
 
 
 
 
 
 
183
 
184
- // Exporting raw client for credits check in App.js
185
- getSupabaseClient: () => supabase
186
  };
 
1
  import { createClient } from '@supabase/supabase-js';
2
 
 
3
  let supabase = null;
4
+ const activeProjects = new Map();
5
 
6
  export const initDB = () => {
7
+ supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY);
 
 
 
 
 
 
 
 
8
  };
9
 
 
 
10
  export const StateManager = {
11
+ getProject: async (projectId) => {
12
+ if (activeProjects.has(projectId)) return activeProjects.get(projectId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ const { data: proj } = await supabase.from('projects').select('*').eq('id', projectId).single();
15
+ if (!proj) return null;
16
+
17
+ // Fetch the 2 most recent chunks (up to 40 messages) for AI context
18
+ const { data: chunks } = await supabase.from('message_chunks')
19
+ .select('*').eq('project_id', projectId)
20
+ .order('chunk_index', { ascending: false }).limit(2);
21
+
22
+ const memoryObject = {
23
+ ...proj.info,
24
+ id: proj.id,
25
+ userId: proj.user_id,
26
+ workerHistory: (chunks || []).filter(c => c.type === 'worker').reverse().flatMap(c => c.payload),
27
+ pmHistory: (chunks || []).filter(c => c.type === 'pm').reverse().flatMap(c => c.payload),
28
+ commandQueue: proj.info.commandQueue || [],
29
+ failureCount: proj.info.failureCount || 0,
30
+ lastActive: Date.now()
31
+ };
32
 
33
+ activeProjects.set(projectId, memoryObject);
34
+ return memoryObject;
35
+ },
 
 
36
 
37
+ // CUSTOM CHUNK LOGIC: Appends message to chunked entry (max 20 per entry)
38
+ addHistory: async (projectId, type, role, text) => {
39
+ const newMessage = { role, parts: [{ text }] };
 
 
40
 
41
+ // Find latest chunk for this type
42
+ const { data: chunks } = await supabase.from('message_chunks')
43
+ .select('*').eq('project_id', projectId).eq('type', type)
44
+ .order('chunk_index', { ascending: false }).limit(1);
45
+
46
+ const latest = chunks?.[0];
47
+
48
+ if (latest && latest.payload.length < 20) {
49
+ // Append to existing chunk
50
+ const updatedPayload = [...latest.payload, newMessage];
51
+ await supabase.from('message_chunks').update({ payload: updatedPayload }).eq('id', latest.id);
52
+ } else {
53
+ // Create new chunk
54
+ const nextIndex = latest ? latest.chunk_index + 1 : 0;
55
+ await supabase.from('message_chunks').insert({
56
+ project_id: projectId,
57
+ type,
58
+ chunk_index: nextIndex,
59
+ payload: [newMessage]
60
+ });
 
 
 
 
 
 
61
  }
62
+ },
63
+
64
+ updateProject: async (projectId, data) => {
65
+ const current = activeProjects.get(projectId) || {};
66
+ const newData = { ...current, ...data, lastActive: Date.now() };
67
+ activeProjects.set(projectId, newData);
68
+
69
+ const payload = {
70
+ info: {
71
+ title: newData.title,
72
+ status: newData.status,
73
+ stats: newData.stats,
74
+ description: newData.description,
75
+ commandQueue: newData.commandQueue,
76
+ failureCount: newData.failureCount
77
+ }
78
+ };
79
+ await supabase.from('projects').update(payload).eq('id', projectId);
80
+ return newData;
81
+ },
82
+
83
+ queueCommand: async (projectId, input) => {
84
+ const project = await StateManager.getProject(projectId);
85
+ if (!project) return;
86
+ let command = typeof input === 'object' ? input : null;
87
+ if (typeof input === 'string') {
88
+ const code = input.match(/```(?:lua|luau)?([\s\S]*?)```/i);
89
+ const script = input.match(/\[READ_SCRIPT:\s*(.*?)\]/);
90
+ const hier = input.match(/\[READ_HIERARCHY:\s*(.*?)\]/);
91
+ if (code) command = { type: "EXECUTE", payload: code[1].trim() };
92
+ else if (script) command = { type: "READ_SCRIPT", payload: script[1].trim() };
93
+ else if (hier) command = { type: "READ_HIERARCHY", payload: hier[1].trim() };
94
+ else if (input.includes("[READ_LOGS]")) command = { type: "READ_LOGS", payload: null };
95
+ }
96
+ if (command) {
97
+ project.commandQueue.push(command);
98
+ await StateManager.updateProject(projectId, { commandQueue: project.commandQueue });
99
  }
100
+ },
101
+
102
+ popCommand: async (projectId) => {
103
+ const project = await StateManager.getProject(projectId);
104
+ if (!project || !project.commandQueue?.length) return null;
105
+ const cmd = project.commandQueue.shift();
106
+ await StateManager.updateProject(projectId, { commandQueue: project.commandQueue });
107
+ return cmd;
108
+ },
109
 
110
+ getSupabaseClient: () => supabase
 
111
  };