nothumanallowed 16.0.28 → 16.0.29
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 +1 -1
- package/src/constants.mjs +1 -1
- package/src/server/routes/webcraft.mjs +70 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.29",
|
|
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.
|
|
8
|
+
export const VERSION = '16.0.29';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -363,10 +363,22 @@ class SandboxManager {
|
|
|
363
363
|
// The pre-scan in Phase 2 catches most cases. Tier 1 here is the safety
|
|
364
364
|
// net for files generated AFTER pre-scan (e.g. user added a require()
|
|
365
365
|
// during chat) or for transitive crashes that surface only at runtime.
|
|
366
|
-
const
|
|
366
|
+
const rawMissing = [...stderrBuf.matchAll(/Cannot find module ['"]([^'"]+)['"]/g)]
|
|
367
367
|
.map(m => m[1])
|
|
368
368
|
.filter(m => !m.startsWith('.') && !m.startsWith('/') && !m.startsWith('node:'))
|
|
369
|
-
.map(m => m.startsWith('@') ? m.split('/').slice(0, 2).join('/') : m.split('/')[0])
|
|
369
|
+
.map(m => m.startsWith('@') ? m.split('/').slice(0, 2).join('/') : m.split('/')[0])
|
|
370
|
+
// Drop Node.js built-ins — they're in the runtime, can't be installed
|
|
371
|
+
.filter(m => !_NODE_BUILTINS.has(m));
|
|
372
|
+
// Resolve LLM hallucinated aliases (jwt → jsonwebtoken, bcrypt → bcryptjs)
|
|
373
|
+
const missingModules = rawMissing.map(m => _PACKAGE_ALIASES.get(m) || m);
|
|
374
|
+
// Detect aliases — if the shim already covers the resolved name, no install needed
|
|
375
|
+
const aliasedFromShim = rawMissing.filter(m => {
|
|
376
|
+
const real = _PACKAGE_ALIASES.get(m);
|
|
377
|
+
return real && _SHIMMED_MODULES.has(real);
|
|
378
|
+
});
|
|
379
|
+
if (aliasedFromShim.length > 0) {
|
|
380
|
+
emit({ type: 'warn', msg: `LLM hallucinated package names: ${aliasedFromShim.join(', ')} — the runtime shim already aliases these. If you still see this crash, the shim wasn't re-generated. Restart "nha ui".` });
|
|
381
|
+
}
|
|
370
382
|
const uniqueMissing = [...new Set(missingModules)].filter(m => !_SHIMMED_MODULES.has(m));
|
|
371
383
|
if (uniqueMissing.length > 0 && _attempt < MAX_RETRIES) {
|
|
372
384
|
emit({ type: 'phase', phase: 'autofix', msg: `Missing module${uniqueMissing.length === 1 ? '' : 's'}: ${uniqueMissing.join(', ')} — batch installing...` });
|
|
@@ -3658,6 +3670,39 @@ function _installedDeps(projectDir) {
|
|
|
3658
3670
|
} catch { return new Set(); }
|
|
3659
3671
|
}
|
|
3660
3672
|
|
|
3673
|
+
// Node.js built-in modules — these are part of the runtime, not npm packages.
|
|
3674
|
+
// `require('fs')` always works without installation. The pre-scan MUST exclude
|
|
3675
|
+
// these or it tries to `npm install fs` which is meaningless (or worse, picks
|
|
3676
|
+
// up a malicious typosquat). Authoritative list from Node 22 LTS docs.
|
|
3677
|
+
const _NODE_BUILTINS = new Set([
|
|
3678
|
+
'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
|
|
3679
|
+
'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
|
|
3680
|
+
'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector',
|
|
3681
|
+
'inspector/promises', 'module', 'net', 'os', 'path', 'path/posix',
|
|
3682
|
+
'path/win32', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline',
|
|
3683
|
+
'readline/promises', 'repl', 'stream', 'stream/consumers', 'stream/promises',
|
|
3684
|
+
'stream/web', 'string_decoder', 'sys', 'timers', 'timers/promises', 'tls',
|
|
3685
|
+
'trace_events', 'tty', 'url', 'util', 'util/types', 'v8', 'vm', 'wasi',
|
|
3686
|
+
'worker_threads', 'zlib',
|
|
3687
|
+
]);
|
|
3688
|
+
|
|
3689
|
+
// Common LLM hallucinations → real package name. The LLM sometimes invents
|
|
3690
|
+
// shorter aliases for popular packages. We map them transparently so the
|
|
3691
|
+
// generated code "just works" without npm install of fake packages.
|
|
3692
|
+
const _PACKAGE_ALIASES = new Map([
|
|
3693
|
+
['jwt', 'jsonwebtoken'],
|
|
3694
|
+
['bcrypt', 'bcryptjs'],
|
|
3695
|
+
['mongo', 'mongoose'],
|
|
3696
|
+
['postgres', 'pg'],
|
|
3697
|
+
['postgresql', 'pg'],
|
|
3698
|
+
['mysql', 'mysql2'],
|
|
3699
|
+
['env', 'dotenv'],
|
|
3700
|
+
['util-lodash', 'lodash'],
|
|
3701
|
+
['express-cors', 'cors'],
|
|
3702
|
+
['express-helmet', 'helmet'],
|
|
3703
|
+
['express-body-parser', 'body-parser'],
|
|
3704
|
+
]);
|
|
3705
|
+
|
|
3661
3706
|
/** Walk project files and extract all bare-import module names. */
|
|
3662
3707
|
export function _scanProjectImports(projectDir, maxFiles = 500, maxBytes = 200_000) {
|
|
3663
3708
|
const found = new Set();
|
|
@@ -3690,9 +3735,16 @@ export function _scanProjectImports(projectDir, maxFiles = 500, maxBytes = 200_0
|
|
|
3690
3735
|
let m;
|
|
3691
3736
|
while ((m = re.exec(content)) !== null) {
|
|
3692
3737
|
const spec = m[1];
|
|
3738
|
+
// Filter 1: node:fs, node:path etc.
|
|
3693
3739
|
if (spec.startsWith('node:')) continue;
|
|
3740
|
+
// Get the bare package name (handle @scope/name and subpaths)
|
|
3694
3741
|
const pkg = spec.startsWith('@') ? spec.split('/').slice(0, 2).join('/') : spec.split('/')[0];
|
|
3695
|
-
if (pkg)
|
|
3742
|
+
if (!pkg) continue;
|
|
3743
|
+
// Filter 2: skip Node built-ins (fs, path, crypto, http, etc.)
|
|
3744
|
+
if (_NODE_BUILTINS.has(pkg)) continue;
|
|
3745
|
+
// Filter 3: resolve LLM-hallucinated aliases to real packages
|
|
3746
|
+
const real = _PACKAGE_ALIASES.get(pkg) || pkg;
|
|
3747
|
+
found.add(real);
|
|
3696
3748
|
}
|
|
3697
3749
|
}
|
|
3698
3750
|
}
|
|
@@ -4357,12 +4409,26 @@ module.exports.default = proxy;
|
|
|
4357
4409
|
// real implementation, and only falls back to our shim when resolution fails.
|
|
4358
4410
|
// Also supports NHA_OFFLINE_SHIM=1 to no-op any unresolvable module.
|
|
4359
4411
|
const shimList = JSON.stringify(Object.keys(shimFiles).filter(f => f !== 'noop.js').map(f => f.replace(/\.js$/, '')));
|
|
4412
|
+
// ALIAS map: bare names the LLM tends to invent → real package name we shim.
|
|
4413
|
+
// Must stay in sync with _PACKAGE_ALIASES in the parent module so pre-scan
|
|
4414
|
+
// and runtime shim agree on what to resolve.
|
|
4415
|
+
const aliasJson = JSON.stringify({
|
|
4416
|
+
'bcrypt': 'bcryptjs',
|
|
4417
|
+
'jwt': 'jsonwebtoken',
|
|
4418
|
+
'mongo': 'mongoose',
|
|
4419
|
+
'postgres': 'pg',
|
|
4420
|
+
'postgresql': 'pg',
|
|
4421
|
+
'env': 'dotenv',
|
|
4422
|
+
'express-cors': 'cors',
|
|
4423
|
+
'express-helmet': 'helmet',
|
|
4424
|
+
'express-body-parser': 'body-parser',
|
|
4425
|
+
});
|
|
4360
4426
|
const shimIndex = `
|
|
4361
4427
|
const Module = require('module');
|
|
4362
4428
|
const path = require('path');
|
|
4363
4429
|
const __shimDir = ${JSON.stringify(shimDir)};
|
|
4364
4430
|
const SHIM_NAMES = new Set(${shimList});
|
|
4365
|
-
const ALIAS = {
|
|
4431
|
+
const ALIAS = ${aliasJson};
|
|
4366
4432
|
const OFFLINE = process.env.NHA_OFFLINE_SHIM === '1';
|
|
4367
4433
|
const _original = Module._resolveFilename.bind(Module);
|
|
4368
4434
|
|