thrust-cli 1.0.16 → 1.0.18
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.
- package/build/AuthPage.jsx +0 -0
- package/build/asset-manifest.json +13 -0
- package/build/favicon.ico +0 -0
- package/build/index.html +1 -0
- package/build/logo192.png +0 -0
- package/build/logo512.png +0 -0
- package/build/manifest.json +25 -0
- package/build/robots.txt +3 -0
- package/build/static/css/main.3f5f9134.css +4 -0
- package/build/static/css/main.3f5f9134.css.map +1 -0
- package/build/static/css/main.ecc04ee5.css +4 -0
- package/build/static/css/main.ecc04ee5.css.map +1 -0
- package/build/static/js/main.631499e7.js +3 -0
- package/build/static/js/main.631499e7.js.LICENSE.txt +56 -0
- package/build/static/js/main.631499e7.js.map +1 -0
- package/build/static/js/main.728d8aba.js +3 -0
- package/build/static/js/main.728d8aba.js.LICENSE.txt +56 -0
- package/build/static/js/main.728d8aba.js.map +1 -0
- package/build/thrust_black.jpg +0 -0
- package/build/thrust_equal_black.png +0 -0
- package/build/thrust_equal_white.png +0 -0
- package/build/thrust_tracing_black.png +0 -0
- package/build/thrust_tracing_white.png +0 -0
- package/build/thrust_white.jpg +0 -0
- package/package.json +1 -1
- package/utils/daemon.js +92 -14
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/utils/daemon.js
CHANGED
|
@@ -21,8 +21,8 @@ const API_URL = GATEWAY_URL.replace('ws://', 'http://').replace('wss://', 'https
|
|
|
21
21
|
const AUTH_PROXY_URL = "https://everydaycats-thrust-auth-server.hf.space";
|
|
22
22
|
|
|
23
23
|
// --- DEBOUNCE & POLLING TIMERS ---
|
|
24
|
-
const INACTIVITY_DELAY_MS = 15 * 1000;
|
|
25
|
-
const MCP_POLL_INTERVAL_MS = 18 * 1000;
|
|
24
|
+
const INACTIVITY_DELAY_MS = 15 * 1000;
|
|
25
|
+
const MCP_POLL_INTERVAL_MS = 18 * 1000;
|
|
26
26
|
|
|
27
27
|
let currentWatcher = null;
|
|
28
28
|
let inactivityTimer = null;
|
|
@@ -125,9 +125,9 @@ export async function startDaemon(preferredPort) {
|
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
app.use(cors(corsOptionsDelegate));
|
|
128
|
-
app.use(express.json());
|
|
128
|
+
app.use(express.json({ limit: '50mb' }));
|
|
129
129
|
|
|
130
|
-
const frontendPath = path.join(__dirname, '..', '
|
|
130
|
+
const frontendPath = path.join(__dirname, '..', 'build');
|
|
131
131
|
|
|
132
132
|
// ==========================================
|
|
133
133
|
// MCP SERVER ENDPOINTS (FEED-IN PUSH)
|
|
@@ -138,7 +138,6 @@ export async function startDaemon(preferredPort) {
|
|
|
138
138
|
const token = config.auth?.token;
|
|
139
139
|
const projectId = config.activeLeadId;
|
|
140
140
|
|
|
141
|
-
// Extract optional parameters, default to true
|
|
142
141
|
const prd = req.query.prd || 'true';
|
|
143
142
|
const thrust = req.query.thrust || 'true';
|
|
144
143
|
const timeline = req.query.timeline || 'true';
|
|
@@ -148,7 +147,6 @@ export async function startDaemon(preferredPort) {
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
try {
|
|
151
|
-
// Forward parameters to new combined gateway endpoint
|
|
152
150
|
const response = await fetch(`${API_URL}/api/projects/${projectId}/mcp-context?prd=${prd}&thrust=${thrust}&timeline=${timeline}`, {
|
|
153
151
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
154
152
|
});
|
|
@@ -200,7 +198,7 @@ export async function startDaemon(preferredPort) {
|
|
|
200
198
|
saveConfig(config);
|
|
201
199
|
|
|
202
200
|
pollExternalMCPServers();
|
|
203
|
-
fetchInitialMCPContext({ name, url, type: 'http' });
|
|
201
|
+
fetchInitialMCPContext({ name, url, type: 'http' });
|
|
204
202
|
res.json({ success: true });
|
|
205
203
|
});
|
|
206
204
|
|
|
@@ -214,6 +212,52 @@ export async function startDaemon(preferredPort) {
|
|
|
214
212
|
res.json({ success: true });
|
|
215
213
|
});
|
|
216
214
|
|
|
215
|
+
app.get('/api/mcp/check-unity', (req, res) => {
|
|
216
|
+
const config = getConfig();
|
|
217
|
+
const projectId = config.activeLeadId;
|
|
218
|
+
if (!projectId || !config.leads[projectId]?.path) {
|
|
219
|
+
return res.status(400).json({ error: "No active project linked." });
|
|
220
|
+
}
|
|
221
|
+
const activePath = config.leads[projectId].path;
|
|
222
|
+
|
|
223
|
+
// Editor folder requirement ensures Unity treats the file as an Editor script
|
|
224
|
+
const expected1 = path.join(activePath, 'Assets', 'Thrust', 'Editor', 'ThrustMCPBridge.cs');
|
|
225
|
+
const expected2 = path.join(activePath, 'Assets', 'Editor', 'Thrust', 'ThrustMCPBridge.cs');
|
|
226
|
+
|
|
227
|
+
const exists = fs.existsSync(expected1) || fs.existsSync(expected2);
|
|
228
|
+
res.json({ exists });
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
app.post('/api/mcp/install-unity', async (req, res) => {
|
|
232
|
+
const config = getConfig();
|
|
233
|
+
const projectId = config.activeLeadId;
|
|
234
|
+
if (!projectId || !config.leads[projectId]?.path) {
|
|
235
|
+
return res.status(400).json({ error: "No active project linked." });
|
|
236
|
+
}
|
|
237
|
+
const activePath = config.leads[projectId].path;
|
|
238
|
+
const sourceScript = path.join(__dirname, '..', 'mcps', 'ThrustMCPBridge.cs');
|
|
239
|
+
|
|
240
|
+
if (!fs.existsSync(sourceScript)) {
|
|
241
|
+
return res.status(500).json({ error: "Source ThrustMCPBridge.cs not found in thrust-cli/mcps/" });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const targetDir = path.join(activePath, 'Assets', 'Thrust', 'Editor');
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
if (!fs.existsSync(targetDir)) {
|
|
248
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
249
|
+
}
|
|
250
|
+
const targetScript = path.join(targetDir, 'ThrustMCPBridge.cs');
|
|
251
|
+
fs.copyFileSync(sourceScript, targetScript);
|
|
252
|
+
|
|
253
|
+
broadcastLocalLog('system', `✅ Injected ThrustMCPBridge.cs into ${targetDir}`);
|
|
254
|
+
res.json({ success: true, path: targetScript });
|
|
255
|
+
} catch (e) {
|
|
256
|
+
broadcastLocalLog('error', `Failed to inject Unity script: ${e.message}`);
|
|
257
|
+
res.status(500).json({ error: e.message });
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
217
261
|
// ==========================================
|
|
218
262
|
// EXTERNAL MCP DIRECT QUERY (AI OR MANUAL)
|
|
219
263
|
// ==========================================
|
|
@@ -327,6 +371,10 @@ export async function startDaemon(preferredPort) {
|
|
|
327
371
|
const data = await response.json();
|
|
328
372
|
if (!response.ok) throw new Error(data.error);
|
|
329
373
|
|
|
374
|
+
// 👉 NEW: Force the buffer to have content so syncContext doesn't abort!
|
|
375
|
+
// This tells the AI exactly what you just checked off manually.
|
|
376
|
+
fileActivityBuffer += `\n[${new Date().toLocaleTimeString()}] [USER ACTION] Manually marked task as complete: "${taskTitle}"\n`;
|
|
377
|
+
|
|
330
378
|
if (config.leads[projectId]?.path) {
|
|
331
379
|
if (inactivityTimer) clearTimeout(inactivityTimer);
|
|
332
380
|
syncContext(config.leads[projectId].path);
|
|
@@ -455,7 +503,12 @@ export async function startDaemon(preferredPort) {
|
|
|
455
503
|
const data = JSON.parse(message.toString());
|
|
456
504
|
if (data.type === 'frontend_prompt') {
|
|
457
505
|
if (globalWs && globalWs.readyState === WebSocket.OPEN) {
|
|
458
|
-
globalWs.send(JSON.stringify({
|
|
506
|
+
globalWs.send(JSON.stringify({
|
|
507
|
+
type: 'prompt',
|
|
508
|
+
content: data.payload,
|
|
509
|
+
image: data.image,
|
|
510
|
+
projectId: getActiveProject()?.id
|
|
511
|
+
}));
|
|
459
512
|
broadcastLocalLog('sync', `Prompt sent to Cloud Director...`);
|
|
460
513
|
} else {
|
|
461
514
|
broadcastLocalLog('error', `⚠️ Cannot send: Cloud Gateway is offline.`);
|
|
@@ -535,13 +588,11 @@ async function startWatching(projectPath) {
|
|
|
535
588
|
|
|
536
589
|
fileActivityBuffer = "";
|
|
537
590
|
|
|
538
|
-
// Fetch initial MCP states immediately upon watching a project
|
|
539
591
|
const config = getConfig();
|
|
540
592
|
if (config.mcpServers && config.mcpServers.length > 0) {
|
|
541
593
|
config.mcpServers.forEach(server => fetchInitialMCPContext(server));
|
|
542
594
|
}
|
|
543
595
|
|
|
544
|
-
// Start the active polling loop for external services
|
|
545
596
|
if (mcpPollTimer) clearInterval(mcpPollTimer);
|
|
546
597
|
mcpPollTimer = setInterval(pollExternalMCPServers, MCP_POLL_INTERVAL_MS);
|
|
547
598
|
|
|
@@ -549,11 +600,37 @@ async function startWatching(projectPath) {
|
|
|
549
600
|
ignored:[
|
|
550
601
|
/(^|[\/\\])\../,
|
|
551
602
|
'**/node_modules/**', '**/dist/**', '**/build/**',
|
|
552
|
-
// Ignore noisy Unity cache & build folders
|
|
553
603
|
'**/Library/**', '**/Temp/**', '**/Logs/**', '**/obj/**', '**/ProjectSettings/**'
|
|
554
604
|
],
|
|
555
605
|
persistent: true,
|
|
556
|
-
ignoreInitial: true
|
|
606
|
+
ignoreInitial: true,
|
|
607
|
+
ignorePermissionErrors: true // NEW: Ignores restricted Android/Termux subfolders safely
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// NEW: FATAL ERROR HANDLER
|
|
611
|
+
// Catches EACCES constraints before Node.js crashes and forces a UI unlink
|
|
612
|
+
currentWatcher.on('error', async (error) => {
|
|
613
|
+
const isPermissionError = error.code === 'EACCES' || error.code === 'EPERM';
|
|
614
|
+
const msg = isPermissionError
|
|
615
|
+
? `Permission Denied: Cannot watch folder. Access Denied by OS.`
|
|
616
|
+
: `Watcher Error: ${error.message}`;
|
|
617
|
+
|
|
618
|
+
broadcastLocalLog('error', msg);
|
|
619
|
+
|
|
620
|
+
// Force unlink the bad folder
|
|
621
|
+
const cfg = getConfig();
|
|
622
|
+
cfg.activeLeadId = null;
|
|
623
|
+
saveConfig(cfg);
|
|
624
|
+
|
|
625
|
+
if (inactivityTimer) { clearTimeout(inactivityTimer); inactivityTimer = null; }
|
|
626
|
+
if (mcpPollTimer) { clearInterval(mcpPollTimer); mcpPollTimer = null; }
|
|
627
|
+
if (currentWatcher) {
|
|
628
|
+
await currentWatcher.close().catch(() => {});
|
|
629
|
+
currentWatcher = null;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Signal the frontend to drop the user back to the setup wizard
|
|
633
|
+
broadcastLocalLog('force_unlink', 'Folder access denied. Project unlinked safely.');
|
|
557
634
|
});
|
|
558
635
|
|
|
559
636
|
currentWatcher.on('all', (event, filePath) => {
|
|
@@ -563,7 +640,9 @@ async function startWatching(projectPath) {
|
|
|
563
640
|
|
|
564
641
|
triggerDebouncedSync();
|
|
565
642
|
});
|
|
566
|
-
} catch (err) {
|
|
643
|
+
} catch (err) {
|
|
644
|
+
broadcastLocalLog('error', `Failed to initialize watcher: ${err.message}`);
|
|
645
|
+
}
|
|
567
646
|
}
|
|
568
647
|
|
|
569
648
|
function connectWebSocket() {
|
|
@@ -589,7 +668,6 @@ function connectWebSocket() {
|
|
|
589
668
|
try {
|
|
590
669
|
const msg = JSON.parse(data.toString());
|
|
591
670
|
|
|
592
|
-
// NEW: Handle dynamic MCP Queries pushed from the AI Director
|
|
593
671
|
if (msg.type === 'mcp_query' && msg.payload) {
|
|
594
672
|
const { serverName, toolName, targetArg } = msg.payload;
|
|
595
673
|
const targetServer = getConfig().mcpServers?.find(s => s.name.toLowerCase() === serverName.toLowerCase());
|