nothumanallowed 16.0.25 → 16.0.27

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": "16.0.25",
3
+ "version": "16.0.27",
4
4
  "description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '16.0.25';
8
+ export const VERSION = '16.0.27';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -142,6 +142,34 @@ class SandboxManager {
142
142
  return;
143
143
  }
144
144
 
145
+ // ── Phase 0: Pre-flight repair ───────────────────────────────────────
146
+ // Before anything else, check that package.json on disk is valid JSON.
147
+ // If a previous LLM generation wrote a corrupt file (e.g. an HTTP error
148
+ // response leaked as content), Node's package_json_reader would crash
149
+ // with SyntaxError before we even get to load shims. Repair in place.
150
+ const pkgPath = path.join(projectDir, 'package.json');
151
+ if (fs.existsSync(pkgPath)) {
152
+ const pkgRaw = fs.readFileSync(pkgPath, 'utf-8');
153
+ let needsRepair = false;
154
+ let reason = '';
155
+ if (_looksLikeLLMError(pkgRaw)) {
156
+ needsRepair = true;
157
+ reason = 'content looks like an LLM API error response (Access denied / Rate limit / HTML block page)';
158
+ } else {
159
+ try { JSON.parse(pkgRaw); }
160
+ catch (e) { needsRepair = true; reason = `invalid JSON: ${e.message}`; }
161
+ }
162
+ if (needsRepair) {
163
+ emit({ type: 'warn', msg: `package.json corrupt (${reason}) — auto-repairing with minimal fallback so the sandbox can boot.` });
164
+ const projectName = path.basename(projectDir);
165
+ fs.writeFileSync(pkgPath, _fallbackPackageJson(projectName), 'utf-8');
166
+ emit({ type: 'status', msg: `package.json repaired. Original corrupt content backed up to package.json.corrupt-${Date.now()}` });
167
+ try {
168
+ fs.writeFileSync(pkgPath + '.corrupt-' + Date.now(), pkgRaw, 'utf-8');
169
+ } catch {}
170
+ }
171
+ }
172
+
145
173
  // ── Phase 1: Shims ────────────────────────────────────────────────────
146
174
  emit({ type: 'phase', phase: 'shims', msg: 'Injecting runtime shims (pg, redis, mongoose, helmet...)' });
147
175
  const shimDir = path.join(projectDir, '.nha-shims');
@@ -343,9 +371,23 @@ class SandboxManager {
343
371
  }
344
372
  }
345
373
  } else if (missingModules.length > 0 && uniqueMissing.length === 0) {
346
- // All missing modules are shimmable should never reach here because
347
- // the shim handles them transparently. If it does, log the surprise.
348
- emit({ type: 'warn', msg: `Crash mentions modules that ARE shimmed (${missingModules.join(', ')}). The shim index.js may not have been preloaded — check .nha-launcher.js wiring.` });
374
+ // All missing modules are shimmable in THIS version of the CLI. If we
375
+ // reached this branch, the user has an OLDER CLI installed that doesn't
376
+ // know about these shims yet. Surface this loudly with the exact upgrade
377
+ // command — DON'T leave the user staring at a generic crash.
378
+ emit({ type: 'error', msg:
379
+ `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n` +
380
+ `⚠ IL TUO NHA È OBSOLETO — questo crash è già fixato nell'ultima versione.\n` +
381
+ `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n` +
382
+ `Moduli mancanti che la nuova versione gestisce automaticamente:\n` +
383
+ ` → ${missingModules.join(', ')}\n\n` +
384
+ `Aggiorna NHA (3 comandi):\n` +
385
+ ` 1. npm install -g nothumanallowed@latest\n` +
386
+ ` 2. pkill -f "nha-launcher" ; pkill -f "node.*nha"\n` +
387
+ ` 3. nha ui\n\n` +
388
+ `Verifica versione dopo: nha --version (deve essere >= 16.0.25)\n` +
389
+ `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`
390
+ });
349
391
  }
350
392
 
351
393
  // ── Tier 2: runtime errors that need code fix (require/import mismatch,
@@ -633,7 +675,8 @@ const ProjectStore = {
633
675
  if (!_isSafePath(f.name)) continue;
634
676
  const abs = path.join(dir, f.name);
635
677
  ensureDir(path.dirname(abs));
636
- fs.writeFileSync(abs, f.content ?? '', 'utf-8');
678
+ const sanitized = _sanitizeGeneratedFile(f.name, f.content ?? '', projectName);
679
+ fs.writeFileSync(abs, sanitized, 'utf-8');
637
680
  }
638
681
  const meta = {
639
682
  description: description ?? '',
@@ -3435,6 +3478,74 @@ function _patchEntry(projectDir, entryFile, shimDir, port) {
3435
3478
  return '.nha-launcher.js';
3436
3479
  }
3437
3480
 
3481
+ // Detect LLM API error responses that leaked into the file content. When the
3482
+ // LLM endpoint returns 4xx/5xx with a plaintext body (Cloudflare block, rate
3483
+ // limit, "Access temporarily denied"), the response often ends up saved as
3484
+ // file content. Those strings are short, don't start with valid syntax for
3485
+ // the file type, and contain specific marker phrases.
3486
+ const _LLM_ERROR_MARKERS = [
3487
+ /^\s*Access (temporarily )?denied/i,
3488
+ /^\s*Rate ?limit(ed)?/i,
3489
+ /^\s*Service Unavailable/i,
3490
+ /^\s*Internal Server Error/i,
3491
+ /^\s*Too Many Requests/i,
3492
+ /^\s*Bad Gateway/i,
3493
+ /^\s*Gateway Timeout/i,
3494
+ /^\s*<!DOCTYPE html.*Cloudflare/is,
3495
+ /^\s*<html.*<title>.*(Access|Forbidden|Error)/is,
3496
+ /^\s*\{?\s*"error"\s*:\s*"(rate.?limit|quota|unauthorized)"/i,
3497
+ /^\s*error code:\s*\d{3,4}/i,
3498
+ ];
3499
+
3500
+ function _looksLikeLLMError(content) {
3501
+ if (typeof content !== 'string') return false;
3502
+ const head = content.slice(0, 500);
3503
+ return _LLM_ERROR_MARKERS.some(re => re.test(head));
3504
+ }
3505
+
3506
+ // Minimal package.json template — used when the LLM-generated one is corrupted.
3507
+ function _fallbackPackageJson(projectName) {
3508
+ return JSON.stringify({
3509
+ name: String(projectName || 'nha-project').toLowerCase().replace(/[^a-z0-9-]/g, '-'),
3510
+ version: '1.0.0',
3511
+ description: 'NHA-generated project (package.json auto-repaired)',
3512
+ main: 'index.js',
3513
+ type: 'commonjs',
3514
+ scripts: { start: 'node index.js' },
3515
+ dependencies: {},
3516
+ }, null, 2);
3517
+ }
3518
+
3519
+ // Sanitize content before writing to disk. Rejects LLM error responses,
3520
+ // repairs corrupt JSON manifests, and falls back to a minimal template when
3521
+ // content is unsalvageable. This is the LAST line of defense between the LLM
3522
+ // API and the user's filesystem.
3523
+ function _sanitizeGeneratedFile(name, content, projectName) {
3524
+ const isJson = /\.json$/i.test(name) || name.endsWith('package.json');
3525
+ const isPkg = name === 'package.json' || name.endsWith('/package.json');
3526
+
3527
+ if (_looksLikeLLMError(content)) {
3528
+ if (isPkg) return _fallbackPackageJson(projectName);
3529
+ return '/* nha-webcraft: file content from LLM looked like an HTTP error response and was discarded. Re-generate from chat. */';
3530
+ }
3531
+ if (isJson) {
3532
+ try { JSON.parse(content); }
3533
+ catch {
3534
+ if (isPkg) return _fallbackPackageJson(projectName);
3535
+ try {
3536
+ const repaired = content
3537
+ .replace(/,\s*([}\]])/g, '$1')
3538
+ .replace(/([{,]\s*)([A-Za-z_][A-Za-z0-9_]*)\s*:/g, '$1"$2":');
3539
+ JSON.parse(repaired);
3540
+ return repaired;
3541
+ } catch {
3542
+ return '{}';
3543
+ }
3544
+ }
3545
+ }
3546
+ return content;
3547
+ }
3548
+
3438
3549
  // Authoritative list of modules covered by our offline-safe shims.
3439
3550
  // Used by both the pre-scan (to skip them from npm install) and the Tier 1
3440
3551
  // retry (to detect "missing module" stderr that's already covered by a shim).