thrust-cli 1.0.16 → 1.0.17

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thrust-cli",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "The local agent for Thrust AI Director",
5
5
  "type": "module",
6
6
  "homepage": "https://thrust.web.app",
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;
@@ -127,7 +127,7 @@ export async function startDaemon(preferredPort) {
127
127
  app.use(cors(corsOptionsDelegate));
128
128
  app.use(express.json());
129
129
 
130
- const frontendPath = path.join(__dirname, '..', 'frontend');
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
 
@@ -306,7 +304,8 @@ export async function startDaemon(preferredPort) {
306
304
  }
307
305
  });
308
306
 
309
- app.post('/api/tasks/complete', async (req, res) => {
307
+
308
+ app.post('/api/tasks/complete', async (req, res) => {
310
309
  const { taskId, taskTitle } = req.body;
311
310
  const config = getConfig();
312
311
  const token = config.auth?.token;
@@ -327,6 +326,10 @@ export async function startDaemon(preferredPort) {
327
326
  const data = await response.json();
328
327
  if (!response.ok) throw new Error(data.error);
329
328
 
329
+ // 👉 NEW: Force the buffer to have content so syncContext doesn't abort!
330
+ // This tells the AI exactly what you just checked off manually.
331
+ fileActivityBuffer += `\n[${new Date().toLocaleTimeString()}] [USER ACTION] Manually marked task as complete: "${taskTitle}"\n`;
332
+
330
333
  if (config.leads[projectId]?.path) {
331
334
  if (inactivityTimer) clearTimeout(inactivityTimer);
332
335
  syncContext(config.leads[projectId].path);
@@ -535,13 +538,11 @@ async function startWatching(projectPath) {
535
538
 
536
539
  fileActivityBuffer = "";
537
540
 
538
- // Fetch initial MCP states immediately upon watching a project
539
541
  const config = getConfig();
540
542
  if (config.mcpServers && config.mcpServers.length > 0) {
541
543
  config.mcpServers.forEach(server => fetchInitialMCPContext(server));
542
544
  }
543
545
 
544
- // Start the active polling loop for external services
545
546
  if (mcpPollTimer) clearInterval(mcpPollTimer);
546
547
  mcpPollTimer = setInterval(pollExternalMCPServers, MCP_POLL_INTERVAL_MS);
547
548
 
@@ -549,11 +550,37 @@ async function startWatching(projectPath) {
549
550
  ignored:[
550
551
  /(^|[\/\\])\../,
551
552
  '**/node_modules/**', '**/dist/**', '**/build/**',
552
- // Ignore noisy Unity cache & build folders
553
553
  '**/Library/**', '**/Temp/**', '**/Logs/**', '**/obj/**', '**/ProjectSettings/**'
554
554
  ],
555
555
  persistent: true,
556
- ignoreInitial: true
556
+ ignoreInitial: true,
557
+ ignorePermissionErrors: true // NEW: Ignores restricted Android/Termux subfolders safely
558
+ });
559
+
560
+ // NEW: FATAL ERROR HANDLER
561
+ // Catches EACCES constraints before Node.js crashes and forces a UI unlink
562
+ currentWatcher.on('error', async (error) => {
563
+ const isPermissionError = error.code === 'EACCES' || error.code === 'EPERM';
564
+ const msg = isPermissionError
565
+ ? `Permission Denied: Cannot watch folder. Access Denied by OS.`
566
+ : `Watcher Error: ${error.message}`;
567
+
568
+ broadcastLocalLog('error', msg);
569
+
570
+ // Force unlink the bad folder
571
+ const cfg = getConfig();
572
+ cfg.activeLeadId = null;
573
+ saveConfig(cfg);
574
+
575
+ if (inactivityTimer) { clearTimeout(inactivityTimer); inactivityTimer = null; }
576
+ if (mcpPollTimer) { clearInterval(mcpPollTimer); mcpPollTimer = null; }
577
+ if (currentWatcher) {
578
+ await currentWatcher.close().catch(() => {});
579
+ currentWatcher = null;
580
+ }
581
+
582
+ // Signal the frontend to drop the user back to the setup wizard
583
+ broadcastLocalLog('force_unlink', 'Folder access denied. Project unlinked safely.');
557
584
  });
558
585
 
559
586
  currentWatcher.on('all', (event, filePath) => {
@@ -563,7 +590,9 @@ async function startWatching(projectPath) {
563
590
 
564
591
  triggerDebouncedSync();
565
592
  });
566
- } catch (err) {}
593
+ } catch (err) {
594
+ broadcastLocalLog('error', `Failed to initialize watcher: ${err.message}`);
595
+ }
567
596
  }
568
597
 
569
598
  function connectWebSocket() {
@@ -589,7 +618,6 @@ function connectWebSocket() {
589
618
  try {
590
619
  const msg = JSON.parse(data.toString());
591
620
 
592
- // NEW: Handle dynamic MCP Queries pushed from the AI Director
593
621
  if (msg.type === 'mcp_query' && msg.payload) {
594
622
  const { serverName, toolName, targetArg } = msg.payload;
595
623
  const targetServer = getConfig().mcpServers?.find(s => s.name.toLowerCase() === serverName.toLowerCase());