nothumanallowed 16.0.40 → 16.0.42
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 +143 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.42",
|
|
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.42';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -5027,15 +5027,40 @@ function compilePath(p) {
|
|
|
5027
5027
|
}
|
|
5028
5028
|
|
|
5029
5029
|
function createApp() {
|
|
5030
|
-
|
|
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 = [];
|
|
5031
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
|
+
|
|
5032
5041
|
function use(arg, ...rest) {
|
|
5033
|
-
|
|
5034
|
-
|
|
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
|
+
}
|
|
5035
5053
|
return app;
|
|
5036
5054
|
}
|
|
5037
5055
|
function addRoute(method) {
|
|
5038
|
-
return (
|
|
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
|
+
};
|
|
5039
5064
|
}
|
|
5040
5065
|
const app = async function (req, res) {
|
|
5041
5066
|
const parsed = url.parse(req.url, true);
|
|
@@ -5049,30 +5074,52 @@ function createApp() {
|
|
|
5049
5074
|
res.sendStatus = function (n) { this.statusCode = n; this.end(http.STATUS_CODES[n] || ''); return this; };
|
|
5050
5075
|
res.redirect = function (loc) { this.statusCode = 302; this.setHeader('Location', loc); this.end(); return this; };
|
|
5051
5076
|
}
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
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
|
|
5057
5088
|
if (layer.method !== 'ALL' && layer.method !== req.method) continue;
|
|
5058
|
-
|
|
5059
|
-
if (
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
}
|
|
5070
|
-
|
|
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); }
|
|
5071
5112
|
}
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
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();
|
|
5076
5123
|
};
|
|
5077
5124
|
app.use = use;
|
|
5078
5125
|
app.get = addRoute('GET'); app.post = addRoute('POST'); app.put = addRoute('PUT');
|
|
@@ -5089,7 +5136,76 @@ function createApp() {
|
|
|
5089
5136
|
const express = createApp;
|
|
5090
5137
|
express.json = function (opts) { return async (req, res, next) => { if (/json/i.test(req.headers['content-type'] || '')) req.body = await parseBody(req); next(); }; };
|
|
5091
5138
|
express.urlencoded = function (opts) { return async (req, res, next) => { if (/urlencoded/i.test(req.headers['content-type'] || '')) req.body = await parseBody(req); next(); }; };
|
|
5092
|
-
|
|
5139
|
+
|
|
5140
|
+
// MIME type table for static files
|
|
5141
|
+
const _MIME = {
|
|
5142
|
+
'.html': 'text/html; charset=utf-8',
|
|
5143
|
+
'.htm': 'text/html; charset=utf-8',
|
|
5144
|
+
'.css': 'text/css; charset=utf-8',
|
|
5145
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
5146
|
+
'.mjs': 'application/javascript; charset=utf-8',
|
|
5147
|
+
'.json': 'application/json; charset=utf-8',
|
|
5148
|
+
'.png': 'image/png',
|
|
5149
|
+
'.jpg': 'image/jpeg',
|
|
5150
|
+
'.jpeg': 'image/jpeg',
|
|
5151
|
+
'.gif': 'image/gif',
|
|
5152
|
+
'.webp': 'image/webp',
|
|
5153
|
+
'.svg': 'image/svg+xml',
|
|
5154
|
+
'.ico': 'image/x-icon',
|
|
5155
|
+
'.woff': 'font/woff',
|
|
5156
|
+
'.woff2': 'font/woff2',
|
|
5157
|
+
'.ttf': 'font/ttf',
|
|
5158
|
+
'.otf': 'font/otf',
|
|
5159
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
5160
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
5161
|
+
'.xml': 'application/xml',
|
|
5162
|
+
'.pdf': 'application/pdf',
|
|
5163
|
+
'.map': 'application/json',
|
|
5164
|
+
'.mp4': 'video/mp4',
|
|
5165
|
+
'.webm': 'video/webm',
|
|
5166
|
+
'.mp3': 'audio/mpeg',
|
|
5167
|
+
'.wav': 'audio/wav',
|
|
5168
|
+
};
|
|
5169
|
+
|
|
5170
|
+
// Real express.static implementation — serves files from disk with proper
|
|
5171
|
+
// MIME types. Supports index.html for directory requests, path traversal
|
|
5172
|
+
// protection. Falls through to next() when the file doesn't exist.
|
|
5173
|
+
express.static = function (root, opts) {
|
|
5174
|
+
opts = opts || {};
|
|
5175
|
+
const indexFile = opts.index === false ? null : (opts.index || 'index.html');
|
|
5176
|
+
const rootAbs = require('path').resolve(root);
|
|
5177
|
+
return function (req, res, next) {
|
|
5178
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
5179
|
+
try {
|
|
5180
|
+
let relPath = decodeURIComponent((req.path || req.url || '/').split('?')[0]);
|
|
5181
|
+
// Path traversal protection
|
|
5182
|
+
if (relPath.includes('\\\\0') || relPath.includes('..')) return next();
|
|
5183
|
+
if (relPath.startsWith('/')) relPath = relPath.slice(1);
|
|
5184
|
+
const abs = require('path').resolve(rootAbs, relPath);
|
|
5185
|
+
// Ensure resolved path stays within root
|
|
5186
|
+
if (!abs.startsWith(rootAbs)) return next();
|
|
5187
|
+
let stat;
|
|
5188
|
+
try { stat = require('fs').statSync(abs); } catch { return next(); }
|
|
5189
|
+
let filePath = abs;
|
|
5190
|
+
if (stat.isDirectory()) {
|
|
5191
|
+
if (!indexFile) return next();
|
|
5192
|
+
filePath = require('path').join(abs, indexFile);
|
|
5193
|
+
try { stat = require('fs').statSync(filePath); } catch { return next(); }
|
|
5194
|
+
if (!stat.isFile()) return next();
|
|
5195
|
+
}
|
|
5196
|
+
const ext = require('path').extname(filePath).toLowerCase();
|
|
5197
|
+
const mime = _MIME[ext] || 'application/octet-stream';
|
|
5198
|
+
res.setHeader('Content-Type', mime);
|
|
5199
|
+
res.setHeader('Content-Length', String(stat.size));
|
|
5200
|
+
res.setHeader('Cache-Control', 'public, max-age=0');
|
|
5201
|
+
if (req.method === 'HEAD') return res.end();
|
|
5202
|
+
require('fs').createReadStream(filePath).pipe(res);
|
|
5203
|
+
} catch (e) {
|
|
5204
|
+
return next();
|
|
5205
|
+
}
|
|
5206
|
+
};
|
|
5207
|
+
};
|
|
5208
|
+
|
|
5093
5209
|
express.Router = function () { const r = createApp(); return r; };
|
|
5094
5210
|
module.exports = express;
|
|
5095
5211
|
module.exports.default = express;
|