everydaycats commited on
Commit
f48f6ae
·
verified ·
1 Parent(s): f3d7cdd

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +126 -186
app.js CHANGED
@@ -1,235 +1,175 @@
1
- // server.js
2
  import express from 'express';
3
  import bodyParser from 'body-parser';
4
  import cors from 'cors';
5
  import { StateManager } from './stateManager.js';
6
  import { AIEngine } from './aiEngine.js';
7
- import fs from 'fs';
8
 
9
  const app = express();
10
- const PORT = process.env.PORT || 7860;
11
-
12
- // Load prompts for dynamic usage
13
- const sysPrompts = JSON.parse(fs.readFileSync('./prompts.json', 'utf8'));
14
-
15
  app.use(cors());
16
- app.use(bodyParser.json({ limit: '50mb' })); // Increased limit for images
 
17
 
18
- // Middleware: Validate User/Project IDs
19
- const validateRequest = (req, res, next) => {
20
- const { userId, projectId } = req.body;
21
- if (!userId || !projectId) {
22
- return res.status(400).json({ error: "Missing userId or projectId" });
23
- }
24
- next();
25
  };
26
 
27
- /**
28
- * 1. NEW PROJECT
29
- * Flow: PM (GDD) -> PM (Tasks) -> Worker (Task 1)
30
- */
31
- app.post('/new/project', validateRequest, async (req, res) => {
32
  const { userId, projectId, description } = req.body;
 
 
 
 
33
 
34
- try {
35
- // 1. Generate GDD with PM (High Thinking)
36
- const pmHistory = [];
37
- const gddPrompt = `Create a comprehensive GDD for: ${description}`;
38
- const gddResponse = await AIEngine.callPM(pmHistory, gddPrompt);
39
-
40
- pmHistory.push({ role: 'user', parts: [{ text: gddPrompt }] });
41
- pmHistory.push({ role: 'model', parts: [{ text: gddResponse }] });
42
-
43
- // 2. Generate Initial Tasks
44
- const taskPrompt = "Based on the GDD, generate a prioritized list of initial technical tasks. Output them as a JSON list.";
45
- const taskResponse = await AIEngine.callPM(pmHistory, taskPrompt);
46
-
47
- pmHistory.push({ role: 'user', parts: [{ text: taskPrompt }] });
48
- pmHistory.push({ role: 'model', parts: [{ text: taskResponse }] });
49
 
50
- // 3. Initialize Worker with GDD Context and Execute First Task
51
- const workerHistory = [];
52
- const initialWorkerPrompt = `Here is the GDD: ${gddResponse}. \n\n Here are the tasks: ${taskResponse}. \n\n EXECUTE THE FIRST TASK NOW.`;
53
- const workerResponse = await AIEngine.callWorker(workerHistory, initialWorkerPrompt);
54
 
55
- workerHistory.push({ role: 'user', parts: [{ text: initialWorkerPrompt }] });
56
- workerHistory.push({ role: 'model', parts: [{ text: workerResponse }] });
 
 
 
57
 
58
- // Save State
59
- await StateManager.updateProject(projectId, {
60
- userId,
61
- pmHistory,
62
- workerHistory,
63
- gdd: gddResponse,
64
- failureCount: 0
65
- });
66
 
67
- // Queue the result for the plugin
68
- StateManager.queueCommand(projectId, workerResponse);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
- res.json({ success: true, message: "Project initialized", gddPreview: gddResponse.substring(0, 200) });
 
 
 
 
 
 
71
 
72
- } catch (err) {
73
- console.error(err);
74
- res.status(500).json({ error: "AI Processing Failed" });
75
- }
76
- });
77
 
78
- /**
79
- * 2. FEEDBACK (The Main Loop Endpoint)
80
- * Accepts text + optional image. Hits Worker. Checks for escalation.
81
- */
82
  app.post('/project/feedback', async (req, res) => {
83
  const { projectId, prompt, hierarchyContext, scriptContext, logContext } = req.body;
84
 
85
  const project = await StateManager.getProject(projectId);
86
- if (!project) return res.status(404).json({ error: "Project not found." });
87
 
88
- // Build Context String based on what was sent
89
- let contextStr = "";
90
- if (prompt) contextStr += `USER: ${prompt}\n`;
91
-
92
- // If Plugin sent Hierarchy
93
- if (hierarchyContext) {
94
- contextStr += `[SYSTEM] HIERARCHY SCAN (${hierarchyContext.rootName}):\n${hierarchyContext.tree}\n`;
95
- }
96
- // If Plugin sent Script Source
97
- if (scriptContext) {
98
- contextStr += `[SYSTEM] SCRIPT SOURCE (${scriptContext.targetName}):\n${scriptContext.scriptSource}\n`;
99
- }
100
- // If Plugin sent Logs
101
- if (logContext) {
102
- contextStr += `[SYSTEM] CONSOLE LOGS:\n${logContext.logs}\n`;
103
- }
104
 
105
- console.log(`[${projectId}] Input Received. Processing...`);
106
 
107
  // Call Worker
108
- const response = await AIEngine.callWorker(project.workerHistory, contextStr);
109
 
110
- // Standard History Update
111
- project.workerHistory.push({ role: 'user', parts: [{ text: contextStr }] });
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
113
  await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
114
-
115
- // Parse and Queue the next move (Execute code or Fetch more info)
116
  await StateManager.queueCommand(projectId, response);
117
 
118
- res.json({ success: true, message: "Context received. Worker processing." });
119
  });
120
 
121
- /**
122
- * 3. INTERNAL PING WORKER
123
- * PM sends guidance to Worker.
124
- */
125
- app.post('/internal/ping/worker', validateRequest, async (req, res) => {
126
- const { projectId, guidancePrompt } = req.body;
 
127
  const project = await StateManager.getProject(projectId);
128
- if (!project) return res.status(404).json({ error: "Project not found" });
129
 
130
- // Inject PM Context (GDD/History) into the message if requested
131
- const fullContextPrompt = `PM CONTEXT: ${JSON.stringify(project.pmHistory)}. \n GUIDANCE: ${guidancePrompt}`;
132
 
133
- const response = await AIEngine.callWorker(project.workerHistory, fullContextPrompt);
134
- StateManager.queueCommand(projectId, response);
135
-
136
- res.json({ success: true, message: "Guidance sent to Worker" });
137
- });
138
 
139
- /**
140
- * 4. INTERNAL PING PM
141
- * Worker signals distress -> PM responds.
142
- */
143
- app.post('/internal/ping/pm', validateRequest, async (req, res) => {
144
- const { projectId, distressMessage } = req.body;
145
- const project = await StateManager.getProject(projectId);
146
-
147
- // PM analyzes distress
148
- const pmResponse = await AIEngine.callPM(project.pmHistory, `Worker Distress: ${distressMessage}. Provide solution.`);
149
-
150
- // Logic: Decide to reset worker OR guide worker
151
- if (pmResponse.includes("RESET_RECOMMENDED")) {
152
- // Call reset endpoint logic (internal function call)
153
- project.workerHistory = [{ role: 'user', parts: [{ text: sysPrompts.worker_reset_prompt.replace('{{INSTRUCTION}}', pmResponse) }] }];
154
- await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
155
- res.json({ action: "RESET", guidance: pmResponse });
156
  } else {
157
- // Just guide
158
- const workerResp = await AIEngine.callWorker(project.workerHistory, `PM FIX: ${pmResponse}`);
159
- StateManager.queueCommand(projectId, workerResp);
160
- res.json({ action: "GUIDANCE", guidance: pmResponse });
161
  }
162
  });
163
 
164
- /**
165
- * 5. PROJECT RESET
166
- * Resets the worker thread completely.
167
- */
168
- app.post('/project/reset', validateRequest, async (req, res) => {
169
- const { projectId } = req.body;
170
  const project = await StateManager.getProject(projectId);
171
 
172
- // Clear history, keep GDD context
173
- const resetPrompt = `System reset. Current GDD: ${project.gdd}. Await instructions.`;
174
-
175
- await StateManager.updateProject(projectId, {
176
- workerHistory: [{ role: 'user', parts: [{ text: resetPrompt }] }],
177
- failureCount: 0
178
- });
179
-
180
- res.json({ success: true, message: "Worker thread reset" });
181
- });
182
-
183
- /**
184
- * 6. PROJECT PING (The Roblox Plugin Endpoint)
185
- * Plugin calls this to fetch executable code.
186
- */
187
- /*
188
- app.post('/project/ping', validateRequest, async (req, res) => {
189
- const { projectId } = req.body;
190
- const project = await StateManager.getProject(projectId);
191
-
192
- if (!project || !project.commandQueue || project.commandQueue.length === 0) {
193
- return res.json({ status: "IDLE" });
194
- }
195
-
196
- // Pop the oldest command
197
- const command = project.commandQueue.shift();
198
- await StateManager.updateProject(projectId, { commandQueue: project.commandQueue });
199
 
200
- // Return the whole response, but specifically parsed code for execution
201
- res.json({
202
- status: "EXECUTE",
203
- fullResponse: command.raw,
204
- codeSnippet: command.code // The markdown snippet extracted in StateManager
205
- });
206
- });
207
- */
208
-
209
- /**
210
- * 7. INACTIVITY CLEANUP
211
- */
212
- app.delete('/internal/cleanup', async (req, res) => {
213
- const count = StateManager.cleanupInactivity();
214
- res.json({ success: true, removed: count });
215
- });
216
-
217
- app.post('/project/ping', async (req, res) => {
218
- const { projectId } = req.body;
219
- const command = await StateManager.popCommand(projectId);
220
 
221
- if (command) {
222
- // Sends: { action: "EXECUTE", target: "...", code: "..." }
223
- res.json({
224
- action: command.type,
225
- target: command.payload,
226
- code: command.type === 'EXECUTE' ? command.payload : null
227
- });
228
- } else {
229
- res.json({ action: "IDLE" });
230
- }
 
231
  });
232
 
233
- app.listen(PORT, () => {
234
- console.log(`AI Builder Backend running on port ${PORT}`);
235
- });
 
 
1
  import express from 'express';
2
  import bodyParser from 'body-parser';
3
  import cors from 'cors';
4
  import { StateManager } from './stateManager.js';
5
  import { AIEngine } from './aiEngine.js';
 
6
 
7
  const app = express();
 
 
 
 
 
8
  app.use(cors());
9
+ app.use(bodyParser.json({ limit: '50mb' }));
10
+ const PORT = process.env.PORT || 7860;
11
 
12
+ // Helper to format Roblox context for AI
13
+ const formatContext = (ctx) => {
14
+ let s = "";
15
+ if (ctx.hierarchyContext) s += `\nCURRENT HIERARCHY:\n${ctx.hierarchyContext.tree}\n`;
16
+ if (ctx.scriptContext) s += `\nEDITING SCRIPT: ${ctx.scriptContext.targetName}\nSOURCE:\n${ctx.scriptContext.scriptSource}\n`;
17
+ if (ctx.logContext) s += `\nRECENT LOGS (Errors/Output):\n${ctx.logContext.logs}\n`;
18
+ return s;
19
  };
20
 
21
+ // =======================================================
22
+ // 1. NEW PROJECT (Non-Blocking / Background)
23
+ // =======================================================
24
+ app.post('/new/project', async (req, res) => {
 
25
  const { userId, projectId, description } = req.body;
26
+
27
+ if (!userId || !projectId || !description) {
28
+ return res.status(400).json({ error: "Missing required fields." });
29
+ }
30
 
31
+ // Initialize State immediately so Ping works
32
+ await StateManager.updateProject(projectId, {
33
+ userId,
34
+ status: "INITIALIZING", // Flag to track state
35
+ pmHistory: [],
36
+ workerHistory: []
37
+ });
 
 
 
 
 
 
 
 
38
 
39
+ console.log(`[${projectId}] Request accepted. Starting Background Init...`);
 
 
 
40
 
41
+ // 1. Respond IMMEDIATELY to client
42
+ res.json({
43
+ success: true,
44
+ message: "Project initialization started in background. The AI will start outputting commands shortly."
45
+ });
46
 
47
+ // 2. Run Heavy AI Logic asynchronously (Fire & Forget)
48
+ startBackgroundInit(projectId, description).catch(err => {
49
+ console.error(`[${projectId}] CRITICAL INIT FAILURE:`, err);
50
+ // Queue an error message to the plugin console so the user knows it failed
51
+ StateManager.queueCommand(projectId, `print("CRITICAL AI ERROR: Project Init Failed. ${err.message}")`);
52
+ });
53
+ });
 
54
 
55
+ // The Background Logic
56
+ async function startBackgroundInit(projectId, description) {
57
+ const pmHist = [];
58
+
59
+ // A. PM Generates GDD
60
+ console.log(`[${projectId}] Generating GDD...`);
61
+ const gdd = await AIEngine.callPM(pmHist, `New Project: ${description}. Generate GDD.`);
62
+ pmHist.push({ role: 'user', parts: [{ text: description }] });
63
+ pmHist.push({ role: 'model', parts: [{ text: gdd }] });
64
+
65
+ // B. PM Generates First Task
66
+ console.log(`[${projectId}] Generating Tasks...`);
67
+ const tasks = await AIEngine.callPM(pmHist, "Create a JSON list of the first 3 technical tasks.");
68
+
69
+ // C. Worker Starts Task 1
70
+ console.log(`[${projectId}] Worker executing Task 1...`);
71
+ const workerHist = [];
72
+ const firstPrompt = `GDD: ${gdd}\nTasks: ${tasks}\n\nStart Task 1.`;
73
+ const code = await AIEngine.callWorker(workerHist, firstPrompt);
74
+
75
+ workerHist.push({ role: 'user', parts: [{ text: firstPrompt }] });
76
+ workerHist.push({ role: 'model', parts: [{ text: code }] });
77
 
78
+ // Update State
79
+ await StateManager.updateProject(projectId, {
80
+ pmHistory: pmHist,
81
+ workerHistory: workerHist,
82
+ gdd: gdd,
83
+ status: "ACTIVE"
84
+ });
85
 
86
+ // Queue the code for the plugin
87
+ await StateManager.queueCommand(projectId, code);
88
+ console.log(`[${projectId}] Init Complete. First command queued.`);
89
+ }
 
90
 
91
+ // =======================================================
92
+ // 2. FEEDBACK
93
+ // =======================================================
 
94
  app.post('/project/feedback', async (req, res) => {
95
  const { projectId, prompt, hierarchyContext, scriptContext, logContext } = req.body;
96
 
97
  const project = await StateManager.getProject(projectId);
98
+ if (!project) return res.status(404).json({ error: "Project not found. Run /new/project first." });
99
 
100
+ const fullInput = `USER: ${prompt || "Automatic Feedback"}` + formatContext({ hierarchyContext, scriptContext, logContext });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
+ console.log(`[${projectId}] Feedback received. Processing...`);
103
 
104
  // Call Worker
105
+ const response = await AIEngine.callWorker(project.workerHistory, fullInput);
106
 
107
+ // Check Escalation
108
+ if (response.includes("STATUS: ESCALATE_TO_PM") || (project.failureCount > 2)) {
109
+ console.log(`[${projectId}] Escalating to PM...`);
110
+ const guidance = await AIEngine.callPM(project.pmHistory, `Worker Stuck. Input: ${fullInput}`);
111
+ const fixed = await AIEngine.callWorker(project.workerHistory, `PM GUIDANCE: ${guidance}`);
112
+
113
+ await StateManager.updateProject(projectId, { failureCount: 0 });
114
+ project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
115
+ project.workerHistory.push({ role: 'model', parts: [{ text: fixed }] });
116
+ await StateManager.queueCommand(projectId, fixed);
117
+ return res.json({ success: true, message: "Escalated & Fixed." });
118
+ }
119
+
120
+ // Normal Success
121
+ project.workerHistory.push({ role: 'user', parts: [{ text: fullInput }] });
122
  project.workerHistory.push({ role: 'model', parts: [{ text: response }] });
123
  await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
 
 
124
  await StateManager.queueCommand(projectId, response);
125
 
126
+ res.json({ success: true, message: "Input Processed." });
127
  });
128
 
129
+ // =======================================================
130
+ // 3. INTERNAL / PING
131
+ // =======================================================
132
+ app.post('/project/ping', async (req, res) => {
133
+ const { projectId } = req.body;
134
+
135
+ // We check project existence here to avoid crashing on random pings
136
  const project = await StateManager.getProject(projectId);
137
+ if (!project) return res.json({ action: "IDLE" });
138
 
139
+ const command = await StateManager.popCommand(projectId);
 
140
 
141
+ if (command) {
142
+ // Parse raw command string into specific actions for the plugin
143
+ let action = command.type; // EXECUTE, READ_SCRIPT, etc.
144
+ let target = command.payload;
145
+ let code = (command.type === 'EXECUTE') ? command.payload : null;
146
 
147
+ res.json({ action, target, code });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  } else {
149
+ res.json({ action: "IDLE" });
 
 
 
150
  }
151
  });
152
 
153
+ // Direct PM Intervention Endpoint
154
+ app.post('/internal/ping/pm', async (req, res) => {
155
+ const { projectId, distressMessage } = req.body;
 
 
 
156
  const project = await StateManager.getProject(projectId);
157
 
158
+ if(!project) return res.status(404).json({error: "Project not found"});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ console.log(`[${projectId}] Direct PM Intervention: ${distressMessage}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+ // PM analyzes distress
163
+ const pmResponse = await AIEngine.callPM(project.pmHistory, `DIRECT INTERVENTION: ${distressMessage}. Provide solution.`);
164
+
165
+ // Guide worker
166
+ const workerResp = await AIEngine.callWorker(project.workerHistory, `PM INTERVENTION: ${pmResponse}`);
167
+
168
+ project.workerHistory.push({ role: 'model', parts: [{ text: workerResp }] });
169
+ await StateManager.updateProject(projectId, { workerHistory: project.workerHistory });
170
+ await StateManager.queueCommand(projectId, workerResp);
171
+
172
+ res.json({ success: true, message: "PM Intervened." });
173
  });
174
 
175
+ app.listen(PORT, () => console.log(`🚀 AI Server running on port ${PORT}`));