nothumanallowed 15.1.2 → 15.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "15.1.2",
3
+ "version": "15.1.4",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -243,62 +243,93 @@ async function cmdSelfUpdate() {
243
243
  return;
244
244
  }
245
245
 
246
- info(`Updating: ${npmCheck.current} ${npmCheck.latest}`);
246
+ const target = npmCheck.latest;
247
+ info(`Updating: ${npmCheck.current} → ${target}`);
248
+
249
+ // Step 1: Detect if we need sudo (check if global npm dir is writable)
250
+ let needsSudo = false;
251
+ try {
252
+ const globalDir = execSync('npm root -g 2>/dev/null', { encoding: 'utf-8', timeout: 5000 }).trim();
253
+ if (globalDir) {
254
+ try {
255
+ const fs2 = await import('fs');
256
+ fs2.default.accessSync(globalDir, fs2.constants.W_OK);
257
+ } catch { needsSudo = true; }
258
+ }
259
+ } catch { needsSudo = true; }
247
260
 
248
- // Clear npm cache first to avoid stale versions
249
- try { execSync('npm cache clean --force 2>/dev/null', { timeout: 15000, stdio: 'ignore' }); } catch {}
250
- try { execSync('sudo npm cache clean --force 2>/dev/null', { timeout: 15000, stdio: 'ignore' }); } catch {}
261
+ const sudo = needsSudo ? 'sudo ' : '';
251
262
 
252
- // Install exact version (not @latest tag which can be stale)
253
- const target = npmCheck.latest;
254
- const cmds = [
255
- `npm install -g nothumanallowed@${target}`,
256
- `sudo npm install -g nothumanallowed@${target}`,
257
- ];
263
+ // Step 2: Clean npm cache (the #1 cause of stale installs)
264
+ info('Cleaning npm cache...');
265
+ try { execSync(`${sudo}npm cache clean --force 2>/dev/null`, { timeout: 20000, stdio: 'ignore' }); } catch {}
266
+
267
+ // Step 3: Remove old package first (prevents stale symlinks)
268
+ info('Removing old version...');
269
+ try { execSync(`${sudo}npm uninstall -g nothumanallowed 2>/dev/null`, { timeout: 30000, stdio: 'ignore' }); } catch {}
270
+
271
+ // Step 4: Install exact version (NEVER use @latest — it can be stale)
272
+ info(`Installing v${target}...`);
273
+ const installCmd = `${sudo}npm install -g nothumanallowed@${target} --prefer-online --no-cache`;
274
+ try {
275
+ const output = execSync(installCmd, { encoding: 'utf-8', timeout: 120_000, stdio: ['inherit', 'pipe', 'pipe'] });
258
276
 
259
- for (const cmd of cmds) {
277
+ // Step 5: Verify the installation actually worked
278
+ let installedVersion = '';
260
279
  try {
261
- const usingSudo = cmd.startsWith('sudo');
262
- if (usingSudo) info('Retrying with sudo...');
263
- const output = execSync(cmd, { encoding: 'utf-8', timeout: 120_000, stdio: ['inherit', 'pipe', 'pipe'] });
264
- if (output.includes('nothumanallowed@')) {
265
- ok(`Updated to v${npmCheck.latest}!`);
266
- if (usingSudo) info('(sudo was required on this system)');
267
- // Auto-restart daemon if it was running (so Telegram/Discord reconnect)
268
- try {
269
- const { isRunning, stopDaemon } = await import('./services/ops-daemon.mjs');
270
- if (isRunning()) {
271
- info('Restarting ops daemon...');
272
- await stopDaemon();
273
- // Use the NEW nha binary to restart
274
- const { execSync: execRestart } = await import('child_process');
275
- try {
276
- execRestart('nha ops start', { timeout: 10_000, stdio: 'inherit' });
277
- ok('Ops daemon restarted (Telegram/Discord reconnected).');
278
- } catch {
279
- warn('Could not auto-restart daemon. Run: nha ops start');
280
- }
281
- }
282
- } catch {}
283
- return;
280
+ installedVersion = execSync('node -e "import(\'nothumanallowed/src/constants.mjs\').then(m=>console.log(m.VERSION)).catch(()=>{})" 2>/dev/null || true', { encoding: 'utf-8', timeout: 5000 }).trim();
281
+ } catch {}
282
+ if (!installedVersion) {
283
+ try {
284
+ const npmRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
285
+ const constFile = `${npmRoot}/nothumanallowed/src/constants.mjs`;
286
+ const fs2 = await import('fs');
287
+ if (fs2.default.existsSync(constFile)) {
288
+ const match = fs2.default.readFileSync(constFile, 'utf-8').match(/VERSION\s*=\s*'([^']+)'/);
289
+ if (match) installedVersion = match[1];
290
+ }
291
+ } catch {}
292
+ }
293
+
294
+ if (installedVersion === target) {
295
+ ok(`Updated to v${target}!`);
296
+ } else if (output.includes('nothumanallowed@')) {
297
+ ok(`Updated! (npm reports success)`);
298
+ if (installedVersion && installedVersion !== target) {
299
+ warn(`Installed version appears to be ${installedVersion} — try: hash -r && nha version`);
284
300
  }
285
- } catch (e) {
286
- const msg = e.stderr || e.message || '';
287
- // EACCES = permission denied, try sudo next
288
- if (msg.includes('EACCES') || msg.includes('permission denied')) continue;
289
- // Other error — report and stop
290
- fail(`Update failed: ${msg.slice(0, 200)}`);
291
- console.log('');
292
- info('Try manually: sudo npm install -g nothumanallowed@latest');
293
- return;
301
+ } else {
302
+ warn('Install completed but could not verify version.');
294
303
  }
295
- }
296
304
 
297
- fail('Update failed could not install even with sudo.');
298
- console.log('');
299
- info('Try manually:');
300
- info(' sudo npm install -g nothumanallowed@latest');
301
- info('Or fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-errors');
305
+ if (needsSudo) info('(sudo was required on this system)');
306
+
307
+ // Step 6: Auto-restart daemon if running
308
+ try {
309
+ const { isRunning, stopDaemon } = await import('./services/ops-daemon.mjs');
310
+ if (isRunning()) {
311
+ info('Restarting ops daemon...');
312
+ await stopDaemon();
313
+ try {
314
+ execSync(`${sudo}nha ops start`, { timeout: 10_000, stdio: 'inherit' });
315
+ ok('Ops daemon restarted.');
316
+ } catch { warn('Could not auto-restart daemon. Run: nha ops start'); }
317
+ }
318
+ } catch {}
319
+
320
+ // Step 7: Tell user to refresh shell
321
+ console.log('');
322
+ info('If "nha version" still shows the old version, run: hash -r');
323
+
324
+ } catch (e) {
325
+ const msg = e.stderr || e.stdout || e.message || '';
326
+ fail(`Update failed: ${msg.slice(0, 300)}`);
327
+ console.log('');
328
+ info('Try manually:');
329
+ info(` ${sudo}npm cache clean --force`);
330
+ info(` ${sudo}npm install -g nothumanallowed@${target}`);
331
+ info(' hash -r');
332
+ }
302
333
  }
303
334
 
304
335
  // ── nha responder ─────────────────────────────────────────────────────────
@@ -286,8 +286,25 @@ Select the optimal agent pipeline. Output ONLY the JSON.`;
286
286
  const useWebTools = WEB_TOOL_AGENTS.has(agent);
287
287
  let webDataBlock = '';
288
288
 
289
+ // ALWAYS pre-fetch URLs found in the task — every agent needs real data
290
+ const taskUrls = [...(task || '').matchAll(/https?:\/\/[^\s,)]+/gi)].map(m => m[0]);
291
+ if (taskUrls.length > 0 && !webDataBlock) {
292
+ sse({ token: '[Fetching task URLs...]' });
293
+ const urlFetches = await Promise.all(
294
+ taskUrls.slice(0, 3).map(async (url) => {
295
+ sse({ token: `[Fetching: ${url}]` });
296
+ return { url, content: await runFetchUrl(url) };
297
+ })
298
+ );
299
+ const urlBlock = urlFetches.filter(f => f.content).map(f => `### Content from ${f.url}:\n${f.content}`).join('\n\n---\n\n');
300
+ if (urlBlock) {
301
+ webDataBlock = `\n\n## FETCHED WEBSITE CONTENT (THIS IS THE REAL DATA — base your analysis ONLY on this, do NOT invent):\n\n${urlBlock}`;
302
+ }
303
+ sse({ token: '\n' });
304
+ }
305
+
289
306
  // Pre-fetch web data BEFORE the LLM call so the model writes with real facts
290
- if (useWebTools) {
307
+ if (useWebTools && !webDataBlock) {
291
308
  sse({ token: '[Raccolta dati web...]' });
292
309
 
293
310
  const queries = extractSearchQueries(task, stepDef?.prompt);
@@ -461,7 +478,8 @@ function buildKeywordFallback(task, sanitizedTask, hasPdf, pdfName, it) {
461
478
 
462
479
  const hasEmail = /email|mail|inbox|posta/i.test(taskLow);
463
480
  const hasCalendar = /calendar|agenda|calendari|eventi|schedule/i.test(taskLow);
464
- const hasSearch = /cerca|search|notizie|news|ultime|latest|web|internet|tendenz|trend|acquista|compra|dove\s+trovare|where\s+to\s+buy|similar|simile/i.test(taskLow);
481
+ const hasUrl = /https?:\/\/[^\s]+/i.test(taskLow);
482
+ const hasSearch = hasUrl || /cerca|search|notizie|news|ultime|latest|web|internet|tendenz|trend|acquista|compra|dove\s+trovare|where\s+to\s+buy|similar|simile|esamina|analizza.*sito|analisi.*sito|sito\s+web/i.test(taskLow);
465
483
  const hasCanvas = /html|dashboard|visua|report|grafico|chart/i.test(taskLow);
466
484
  const hasGitHub = /github|git\b|issue\b|pull request|\bPR\b/i.test(taskLow);
467
485
  const hasSlack = /slack/i.test(taskLow);