ai-lens 0.8.46 → 0.8.48
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/.commithash +1 -1
- package/cli/status.js +39 -3
- package/client/sender.js +25 -48
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
85bad35
|
package/cli/status.js
CHANGED
|
@@ -281,16 +281,51 @@ function checkCaptureRun(installedTools) {
|
|
|
281
281
|
}
|
|
282
282
|
} catch { /* best effort */ }
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
// GUI PATH test: on macOS, GUI apps (Cursor) get a minimal PATH from launchd,
|
|
285
|
+
// which often lacks paths added by .zshrc/.bashrc (e.g. nvm, miniforge, homebrew).
|
|
286
|
+
// Test the hook command with a stripped-down PATH to surface "env: node: No such file or directory".
|
|
287
|
+
let guiOk = true;
|
|
288
|
+
let guiDetail = '';
|
|
289
|
+
if (process.platform === 'darwin' && isCursor && shellOk) {
|
|
290
|
+
try {
|
|
291
|
+
const guiPath = '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin';
|
|
292
|
+
const guiEnv = { HOME: homedir(), PATH: guiPath, AI_LENS_PROJECTS: join(homedir(), '.ai-lens-status-check-nonexistent'), AI_LENS_STATUS_CHECK: '1' };
|
|
293
|
+
// Preserve AI_LENS_* config vars so capture.js can load config
|
|
294
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
295
|
+
if (k.startsWith('AI_LENS_') && !(k in guiEnv)) guiEnv[k] = v;
|
|
296
|
+
}
|
|
297
|
+
const guiResult = spawnSync('/bin/sh', ['-c', shellCommand], {
|
|
298
|
+
input: shellEvent,
|
|
299
|
+
encoding: 'utf-8',
|
|
300
|
+
timeout: 10_000,
|
|
301
|
+
env: guiEnv,
|
|
302
|
+
windowsHide: true,
|
|
303
|
+
});
|
|
304
|
+
if (guiResult.error) throw guiResult.error;
|
|
305
|
+
guiOk = guiResult.status === 0;
|
|
306
|
+
const stderr = (guiResult.stderr || '').trim();
|
|
307
|
+
guiDetail = guiOk ? 'exit 0' : `Exit code: ${guiResult.status}\nError: ${stderr || '(no stderr)'}`;
|
|
308
|
+
if (!guiOk && stderr.includes('No such file or directory')) {
|
|
309
|
+
guiDetail += '\nHint: node is not in the system PATH. GUI apps (Cursor) cannot find it.\n'
|
|
310
|
+
+ 'Fix: sudo ln -s $(which node) /usr/local/bin/node';
|
|
311
|
+
}
|
|
312
|
+
} catch (err) {
|
|
313
|
+
guiOk = false;
|
|
314
|
+
guiDetail = `Exit code: N/A\nError: ${err.message}`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
toolResults.push({ name, ok: exitOk, cmd: testCmd, exitDetail, captureNote, shellOk, shellCmd: command, shellDetail, shellCaptureNote, guiOk, guiDetail });
|
|
285
319
|
}
|
|
286
320
|
|
|
287
|
-
const allOk = toolResults.every(r => r.ok && r.shellOk);
|
|
288
|
-
const failedTools = toolResults.filter(r => !r.ok || !r.shellOk).map(r => r.name);
|
|
321
|
+
const allOk = toolResults.every(r => r.ok && r.shellOk && r.guiOk);
|
|
322
|
+
const failedTools = toolResults.filter(r => !r.ok || !r.shellOk || !r.guiOk).map(r => r.name);
|
|
289
323
|
const summaryParts = toolResults.map(r => {
|
|
290
324
|
const directStatus = r.ok ? 'OK' : 'FAILED';
|
|
291
325
|
const shellStatus = r.shellOk ? 'OK' : 'FAILED';
|
|
292
326
|
const parts = [`${r.name}: ${directStatus}`];
|
|
293
327
|
if (!r.shellOk) parts[0] += `, shell: ${shellStatus}`;
|
|
328
|
+
if (!r.guiOk) parts[0] += ', GUI PATH: FAILED';
|
|
294
329
|
if (r.captureNote) parts[0] += ` (${r.captureNote})`;
|
|
295
330
|
return parts[0];
|
|
296
331
|
});
|
|
@@ -302,6 +337,7 @@ function checkCaptureRun(installedTools) {
|
|
|
302
337
|
if (r.captureNote) text += `\n Capture log: ${r.captureNote}`;
|
|
303
338
|
text += `\n Shell: ${r.shellCmd} < (test event)\n Shell result: ${r.shellDetail}`;
|
|
304
339
|
if (r.shellCaptureNote) text += `\n Shell capture log: ${r.shellCaptureNote}`;
|
|
340
|
+
if (r.guiDetail) text += `\n GUI PATH test: ${r.guiDetail}`;
|
|
305
341
|
return text;
|
|
306
342
|
}).join('\n\n');
|
|
307
343
|
|
package/client/sender.js
CHANGED
|
@@ -16,8 +16,6 @@ import {
|
|
|
16
16
|
} from 'node:fs';
|
|
17
17
|
import { join, dirname } from 'node:path';
|
|
18
18
|
import { randomUUID } from 'node:crypto';
|
|
19
|
-
import { request as httpsRequest } from 'node:https';
|
|
20
|
-
import { request as httpRequest } from 'node:http';
|
|
21
19
|
import { fileURLToPath } from 'node:url';
|
|
22
20
|
import {
|
|
23
21
|
ensureDataDir,
|
|
@@ -576,53 +574,32 @@ export function filterOversized(batch, maxBytes = MAX_CHUNK_BYTES) {
|
|
|
576
574
|
// HTTP
|
|
577
575
|
// =============================================================================
|
|
578
576
|
|
|
579
|
-
function postEvents(serverUrl, events, identity, authToken) {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
'X-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
const options = {
|
|
600
|
-
hostname: url.hostname,
|
|
601
|
-
port: url.port || (isHttps ? 443 : 80),
|
|
602
|
-
path: url.pathname,
|
|
603
|
-
method: 'POST',
|
|
604
|
-
headers,
|
|
605
|
-
timeout: 30_000,
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
const req = requestFn(options, (res) => {
|
|
609
|
-
let data = '';
|
|
610
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
611
|
-
res.on('end', () => {
|
|
612
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
613
|
-
try { resolve(JSON.parse(data)); }
|
|
614
|
-
catch (e) { reject(new Error(`Invalid JSON response: ${e.message}`)); }
|
|
615
|
-
} else {
|
|
616
|
-
reject(new Error(`Server responded ${res.statusCode}: ${data}`));
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
req.on('error', reject);
|
|
622
|
-
req.on('timeout', () => { req.destroy(); reject(new Error('Request timed out')); });
|
|
623
|
-
req.write(body);
|
|
624
|
-
req.end();
|
|
577
|
+
async function postEvents(serverUrl, events, identity, authToken) {
|
|
578
|
+
const body = JSON.stringify(events);
|
|
579
|
+
const url = `${serverUrl}/api/events`;
|
|
580
|
+
|
|
581
|
+
const { version: clientVersion, commit: clientCommit } = getClientVersion();
|
|
582
|
+
const headers = {
|
|
583
|
+
'Content-Type': 'application/json',
|
|
584
|
+
'X-Client-Version': `${clientVersion}+${clientCommit}`,
|
|
585
|
+
};
|
|
586
|
+
if (identity.email) headers['X-Developer-Git-Email'] = identity.email;
|
|
587
|
+
if (identity.name) headers['X-Developer-Name'] = encodeURIComponent(identity.name);
|
|
588
|
+
if (authToken) headers['X-Auth-Token'] = authToken;
|
|
589
|
+
|
|
590
|
+
const res = await fetch(url, {
|
|
591
|
+
method: 'POST',
|
|
592
|
+
headers,
|
|
593
|
+
body,
|
|
594
|
+
signal: AbortSignal.timeout(30_000),
|
|
625
595
|
});
|
|
596
|
+
|
|
597
|
+
const data = await res.text();
|
|
598
|
+
if (res.ok) {
|
|
599
|
+
try { return JSON.parse(data); }
|
|
600
|
+
catch (e) { throw new Error(`Invalid JSON response: ${e.message}`); }
|
|
601
|
+
}
|
|
602
|
+
throw new Error(`Server responded ${res.status}: ${data}`);
|
|
626
603
|
}
|
|
627
604
|
|
|
628
605
|
// =============================================================================
|