nothumanallowed 16.0.39 → 16.0.41

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.39",
3
+ "version": "16.0.41",
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.39';
8
+ export const VERSION = '16.0.41';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -4198,6 +4198,56 @@ function _patchEntry(projectDir, entryFile, shimDir, port) {
4198
4198
  ` } catch(e) {}`,
4199
4199
  ` return _origEnd.apply(this, arguments);`,
4200
4200
  `};`,
4201
+ `// Monkey-patch Express to neutralize next(falsy) — common LLM bug where`,
4202
+ `// middleware calls next(err) with err === undefined for EVERY request,`,
4203
+ `// causing 500 on all routes including static files. Express treats any`,
4204
+ `// truthy first arg as an error; if it's falsy (null/undefined/0/''),`,
4205
+ `// it's semantically equivalent to next() per Express docs. We rewrite.`,
4206
+ `(function() {`,
4207
+ ` const Module = require('module');`,
4208
+ ` const _origLoad = Module._load;`,
4209
+ ` function wrapMiddleware(fn) {`,
4210
+ ` if (typeof fn !== 'function' || fn.length !== 3) return fn;`,
4211
+ ` const wrapped = function(req, res, next) {`,
4212
+ ` const safeNext = function(err) {`,
4213
+ ` if (err === undefined || err === null || err === false || err === 0) return next();`,
4214
+ ` return next(err);`,
4215
+ ` };`,
4216
+ ` try { return fn.call(this, req, res, safeNext); }`,
4217
+ ` catch (e) { return next(e); }`,
4218
+ ` };`,
4219
+ ` wrapped._nhaWrapped = true;`,
4220
+ ` return wrapped;`,
4221
+ ` }`,
4222
+ ` function patchExpressApp(express) {`,
4223
+ ` if (!express || express._nhaPatched) return express;`,
4224
+ ` try {`,
4225
+ ` // express() returns an app; patch app.use to wrap middleware`,
4226
+ ` const _origExpress = express;`,
4227
+ ` const factory = function(...args) {`,
4228
+ ` const app = _origExpress.apply(this, args);`,
4229
+ ` if (app && typeof app.use === 'function' && !app._nhaUseWrapped) {`,
4230
+ ` const _origUse = app.use;`,
4231
+ ` app.use = function(...uArgs) {`,
4232
+ ` const mapped = uArgs.map(a => (typeof a === 'function' && a.length === 3 && !a._nhaWrapped) ? wrapMiddleware(a) : a);`,
4233
+ ` return _origUse.apply(this, mapped);`,
4234
+ ` };`,
4235
+ ` app._nhaUseWrapped = true;`,
4236
+ ` }`,
4237
+ ` return app;`,
4238
+ ` };`,
4239
+ ` // Preserve static props: Router, json(), urlencoded(), etc.`,
4240
+ ` Object.assign(factory, _origExpress);`,
4241
+ ` factory._nhaPatched = true;`,
4242
+ ` return factory;`,
4243
+ ` } catch { return express; }`,
4244
+ ` }`,
4245
+ ` Module._load = function(name, parent, isMain) {`,
4246
+ ` const result = _origLoad.call(this, name, parent, isMain);`,
4247
+ ` if (name === 'express') return patchExpressApp(result);`,
4248
+ ` return result;`,
4249
+ ` };`,
4250
+ `})();`,
4201
4251
  `// Strip headers that break sandbox iframe preview:`,
4202
4252
  `// - X-Frame-Options: blocks iframe embedding entirely`,
4203
4253
  `// - X-Content-Type-Options: nosniff: when LLM references /js/*.js files`,
@@ -4977,15 +5027,40 @@ function compilePath(p) {
4977
5027
  }
4978
5028
 
4979
5029
  function createApp() {
4980
- const stack = [];
5030
+ // Flat layer list — each handler is its own layer (matches Express semantics).
5031
+ // Layer types: 'normal' (3-arg) or 'error' (4-arg). Path-mounted layers
5032
+ // ('app.use("/api", router)') get a prefix that must match the URL.
5033
+ const layers = [];
4981
5034
  const settings = {};
5035
+
5036
+ function addLayer(method, re, keys, prefix, handler) {
5037
+ const isErrHandler = typeof handler === 'function' && handler.length === 4;
5038
+ layers.push({ method, re, keys, prefix, handler, isErrHandler });
5039
+ }
5040
+
4982
5041
  function use(arg, ...rest) {
4983
- if (typeof arg === 'function') stack.push({ method: 'ALL', re: /.*/, keys: [], handlers: [arg, ...rest] });
4984
- else { const c = compilePath(arg); stack.push({ method: 'ALL', re: c.re, keys: c.keys, handlers: rest.flat() }); }
5042
+ let prefix = '';
5043
+ let handlers = rest;
5044
+ if (typeof arg === 'function') {
5045
+ handlers = [arg, ...rest];
5046
+ } else {
5047
+ prefix = String(arg).replace(/\\/+$/, '');
5048
+ }
5049
+ for (const h of handlers.flat()) {
5050
+ if (typeof h !== 'function') continue;
5051
+ addLayer('ALL', /.*/, [], prefix, h);
5052
+ }
4985
5053
  return app;
4986
5054
  }
4987
5055
  function addRoute(method) {
4988
- return (path, ...handlers) => { const c = compilePath(path); stack.push({ method, re: c.re, keys: c.keys, handlers: handlers.flat() }); return app; };
5056
+ return (p, ...handlers) => {
5057
+ const c = compilePath(p);
5058
+ for (const h of handlers.flat()) {
5059
+ if (typeof h !== 'function') continue;
5060
+ addLayer(method, c.re, c.keys, '', h);
5061
+ }
5062
+ return app;
5063
+ };
4989
5064
  }
4990
5065
  const app = async function (req, res) {
4991
5066
  const parsed = url.parse(req.url, true);
@@ -4999,30 +5074,52 @@ function createApp() {
4999
5074
  res.sendStatus = function (n) { this.statusCode = n; this.end(http.STATUS_CODES[n] || ''); return this; };
5000
5075
  res.redirect = function (loc) { this.statusCode = 302; this.setHeader('Location', loc); this.end(); return this; };
5001
5076
  }
5002
- let i = 0;
5003
- const next = (err) => {
5004
- if (err) { res.statusCode = 500; return res.end('Error: ' + (err.message || err)); }
5005
- while (i < stack.length) {
5006
- const layer = stack[i++];
5077
+
5078
+ // Real Express-style routing chain:
5079
+ // - 3-arg handlers run ONLY when err is null/undefined
5080
+ // - 4-arg handlers run ONLY when err is truthy
5081
+ // - Path-mounted middleware ('/api') only runs if req.path matches prefix
5082
+ // - Method-specific layers (GET/POST/...) only run for that method
5083
+ let idx = 0;
5084
+ function nextLayer(err) {
5085
+ while (idx < layers.length) {
5086
+ const layer = layers[idx++];
5087
+ // Method filter
5007
5088
  if (layer.method !== 'ALL' && layer.method !== req.method) continue;
5008
- const m = req.path.match(layer.re);
5009
- if (!m) continue;
5010
- req.params = {};
5011
- layer.keys.forEach((k, idx) => { req.params[k] = m[idx + 1]; });
5012
- let hIdx = 0;
5013
- const runHandlers = (e) => {
5014
- if (e) return next(e);
5015
- if (hIdx >= layer.handlers.length) return next();
5016
- const h = layer.handlers[hIdx++];
5017
- try { return h.length === 4 ? h(e, req, res, runHandlers) : h(req, res, runHandlers); }
5018
- catch (err) { return next(err); }
5019
- };
5020
- return runHandlers();
5089
+ // Path filter
5090
+ if (layer.prefix) {
5091
+ if (!req.path.startsWith(layer.prefix)) continue;
5092
+ const remaining = req.path.slice(layer.prefix.length);
5093
+ if (remaining && !remaining.startsWith('/')) continue;
5094
+ } else if (layer.re && layer.method !== 'ALL') {
5095
+ // Specific route — must match
5096
+ const m = req.path.match(layer.re);
5097
+ if (!m) continue;
5098
+ req.params = {};
5099
+ layer.keys.forEach((k, i) => { req.params[k] = m[i + 1]; });
5100
+ }
5101
+ // Error chain filter: skip normal handlers when in error, and skip
5102
+ // error handlers when not in error. THIS is the fix for the MySaaS bug.
5103
+ if (err && !layer.isErrHandler) continue;
5104
+ if (!err && layer.isErrHandler) continue;
5105
+
5106
+ try {
5107
+ if (layer.isErrHandler) {
5108
+ return layer.handler(err, req, res, nextLayer);
5109
+ }
5110
+ return layer.handler(req, res, nextLayer);
5111
+ } catch (e) { return nextLayer(e); }
5021
5112
  }
5022
- res.statusCode = 404;
5023
- res.end('Cannot ' + req.method + ' ' + req.path);
5024
- };
5025
- next();
5113
+ // End of chain — default response
5114
+ if (err) {
5115
+ res.statusCode = err.status || err.statusCode || 500;
5116
+ res.end('Error: ' + ((err && err.message) || String(err)));
5117
+ } else {
5118
+ res.statusCode = 404;
5119
+ res.end('Cannot ' + req.method + ' ' + req.path);
5120
+ }
5121
+ }
5122
+ nextLayer();
5026
5123
  };
5027
5124
  app.use = use;
5028
5125
  app.get = addRoute('GET'); app.post = addRoute('POST'); app.put = addRoute('PUT');