Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -106,15 +106,37 @@ function extractCommands(text) {
|
|
| 106 |
return commands;
|
| 107 |
}
|
| 108 |
|
|
|
|
| 109 |
async function registerMorningCron(projectId, offset) {
|
| 110 |
const now = new Date();
|
| 111 |
const target = new Date(now);
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
if (target <= now) target.setDate(target.getDate() + 1);
|
|
|
|
| 114 |
const delayMs = target.getTime() - now.getTime();
|
|
|
|
|
|
|
| 115 |
fetch(`${CRON_REGISTRY_URL}/register`, {
|
| 116 |
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
| 117 |
-
body: JSON.stringify({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
}).catch(()=>{});
|
| 119 |
}
|
| 120 |
|
|
@@ -175,8 +197,40 @@ async function executeCommands(userId, projectId, commands) {
|
|
| 175 |
} catch (e) {}
|
| 176 |
}
|
| 177 |
return flags;
|
| 178 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
|
|
|
| 180 |
app.post('/init-project', async (req, res) => {
|
| 181 |
const { userId, name, description, localPath } = req.body;
|
| 182 |
const { data: lead } = await supabase.from('leads').insert({ user_id: userId, name, description, local_path: localPath, status: 'active', requirements_doc: "Init..." }).select().single();
|
|
@@ -194,6 +248,7 @@ app.post('/init-project', async (req, res) => {
|
|
| 194 |
} catch (err) {}
|
| 195 |
});
|
| 196 |
});
|
|
|
|
| 197 |
|
| 198 |
app.post('/process', async (req, res) => {
|
| 199 |
const { userId, projectId, prompt, context, images, task_type = 'chat' } = req.body;
|
|
|
|
| 106 |
return commands;
|
| 107 |
}
|
| 108 |
|
| 109 |
+
// --- BULLETPROOF CRON MATH ---
|
| 110 |
async function registerMorningCron(projectId, offset) {
|
| 111 |
const now = new Date();
|
| 112 |
const target = new Date(now);
|
| 113 |
+
|
| 114 |
+
// We want exactly 6:00 AM local time.
|
| 115 |
+
// So we calculate the target UTC minutes needed.
|
| 116 |
+
// E.g., Local 6 AM in EST (-5) = 11 AM UTC (660 mins).
|
| 117 |
+
// E.g., Local 6 AM in IST (+5.5) = 12:30 AM UTC (30 mins).
|
| 118 |
+
const targetUtcMinutes = (6 * 60) - (offset * 60);
|
| 119 |
+
|
| 120 |
+
// setUTCHours natively handles fractional minutes and negative hour rollovers!
|
| 121 |
+
target.setUTCHours(0, targetUtcMinutes, 0, 0);
|
| 122 |
+
|
| 123 |
+
// If 6 AM today has already passed, schedule for tomorrow
|
| 124 |
if (target <= now) target.setDate(target.getDate() + 1);
|
| 125 |
+
|
| 126 |
const delayMs = target.getTime() - now.getTime();
|
| 127 |
+
console.log(`⏰ Briefing Scheduled for ${projectId}. Local 6AM occurs in ${(delayMs/1000/60/60).toFixed(2)} hours.`);
|
| 128 |
+
|
| 129 |
fetch(`${CRON_REGISTRY_URL}/register`, {
|
| 130 |
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
| 131 |
+
body: JSON.stringify({
|
| 132 |
+
secret: CRON_SECRET,
|
| 133 |
+
jobId: `briefing_${projectId}`,
|
| 134 |
+
intervalMs: 86400000, // 24 hours
|
| 135 |
+
initialDelay: delayMs,
|
| 136 |
+
webhookUrl: `https://everydaytok-thrust-core-server.hf.space/automated-briefing`,
|
| 137 |
+
leadId: projectId,
|
| 138 |
+
payload: { projectId, timezoneOffset: offset }
|
| 139 |
+
})
|
| 140 |
}).catch(()=>{});
|
| 141 |
}
|
| 142 |
|
|
|
|
| 197 |
} catch (e) {}
|
| 198 |
}
|
| 199 |
return flags;
|
| 200 |
+
};
|
| 201 |
+
|
| 202 |
+
app.post('/init-project', async (req, res) => {
|
| 203 |
+
// 1. Receive timezoneOffset from frontend
|
| 204 |
+
const { userId, name, description, localPath, timezoneOffset = 0 } = req.body;
|
| 205 |
+
|
| 206 |
+
const { data: lead } = await supabase.from('leads').insert({
|
| 207 |
+
user_id: userId, name, description, local_path: localPath, status: 'active', requirements_doc: "Init..."
|
| 208 |
+
}).select().single();
|
| 209 |
+
|
| 210 |
+
res.json({ success: true, leadId: lead.id });
|
| 211 |
+
|
| 212 |
+
setImmediate(async () => {
|
| 213 |
+
try {
|
| 214 |
+
// 2. Inject the explicit timezone offset into the initialization prompt
|
| 215 |
+
const initInput = `PROJECT: ${name}\nDESC: ${description}\nUSER TIMEZONE OFFSET: ${timezoneOffset}\nTask: Init PRD, First Thrust, Schedule Morning Briefing.`;
|
| 216 |
+
|
| 217 |
+
const aiResult = await callAI([], initInput, {}, [], prompts.init_system_prompt, "", SMART_MODEL_ID);
|
| 218 |
+
aiResult.text += `\n<notification>Project '${name}' initialized successfully!</notification>`;
|
| 219 |
+
|
| 220 |
+
await StateManager.addHistory(lead.id, 'user', initInput);
|
| 221 |
+
await StateManager.addHistory(lead.id, 'model', aiResult.text);
|
| 222 |
+
|
| 223 |
+
const cmds = extractCommands(aiResult.text);
|
| 224 |
+
const flags = await executeCommands(userId, lead.id, cmds);
|
| 225 |
+
|
| 226 |
+
if (flags.newThrustId) {
|
| 227 |
+
await dispatchEmail(userId, lead.id, lead.name, flags.newThrustId, flags.newThrustMarkdown);
|
| 228 |
+
}
|
| 229 |
+
} catch (err) {}
|
| 230 |
+
});
|
| 231 |
+
});
|
| 232 |
|
| 233 |
+
/*
|
| 234 |
app.post('/init-project', async (req, res) => {
|
| 235 |
const { userId, name, description, localPath } = req.body;
|
| 236 |
const { data: lead } = await supabase.from('leads').insert({ user_id: userId, name, description, local_path: localPath, status: 'active', requirements_doc: "Init..." }).select().single();
|
|
|
|
| 248 |
} catch (err) {}
|
| 249 |
});
|
| 250 |
});
|
| 251 |
+
*/
|
| 252 |
|
| 253 |
app.post('/process', async (req, res) => {
|
| 254 |
const { userId, projectId, prompt, context, images, task_type = 'chat' } = req.body;
|