nothumanallowed 15.1.18 → 15.1.20
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 +222 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.20",
|
|
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/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 = '15.1.
|
|
8
|
+
export const VERSION = '15.1.20';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -289,19 +289,99 @@ class SandboxManager {
|
|
|
289
289
|
|
|
290
290
|
const matchedPattern = runtimePatterns.find(p => p.re.test(stderrBuf));
|
|
291
291
|
if (matchedPattern && _attempt < MAX_RETRIES) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
292
|
+
emit({ type: 'phase', phase: 'autofix', msg: `Runtime error detected: ${matchedPattern.name} — analyzing...` });
|
|
293
|
+
|
|
294
|
+
// ── Special pre-fix for require/import mismatches ──
|
|
295
|
+
// For "require is not defined" the project is ESM but uses CJS syntax.
|
|
296
|
+
// Easiest fix: flip package.json — REMOVE "type":"module" so CJS works,
|
|
297
|
+
// OR rewrite files. We try the package.json toggle FIRST (cheaper) only
|
|
298
|
+
// when the failing files clearly use `require()`. For the inverse case
|
|
299
|
+
// ("Cannot use import statement outside a module") we ADD "type":"module".
|
|
300
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
301
|
+
const isRequireError = /require is not defined/i.test(stderrBuf);
|
|
302
|
+
const isImportError = /Cannot use import statement outside a module/i.test(stderrBuf);
|
|
303
|
+
|
|
304
|
+
if ((isRequireError || isImportError) && fs.existsSync(pkgPath)) {
|
|
305
|
+
try {
|
|
306
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
307
|
+
if (isImportError && pkg.type !== 'module') {
|
|
308
|
+
pkg.type = 'module';
|
|
309
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
310
|
+
emit({ type: 'status', msg: 'Auto-fix: added "type":"module" to package.json — retrying...' });
|
|
311
|
+
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
312
|
+
}
|
|
313
|
+
if (isRequireError && pkg.type === 'module') {
|
|
314
|
+
// Flip OFF "type":"module" so require() works again
|
|
315
|
+
delete pkg.type;
|
|
316
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
317
|
+
emit({ type: 'status', msg: 'Auto-fix: removed "type":"module" from package.json — retrying...' });
|
|
318
|
+
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
319
|
+
}
|
|
320
|
+
} catch (e) {
|
|
321
|
+
emit({ type: 'warn', msg: `package.json toggle failed: ${e.message.slice(0, 200)}` });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ── Extract file path from stack trace (multiple patterns) ──
|
|
326
|
+
// Try several regex forms to be robust against various Node stack formats.
|
|
327
|
+
const allPaths = new Set();
|
|
328
|
+
// Form A: "at /abs/path/file.js:N:M" or "at file:///abs/path/file.js:N:M"
|
|
329
|
+
for (const m of stderrBuf.matchAll(/(?:file:\/\/)?(\/[^\s:()'"]+\.(?:m?js|cjs|jsx?|tsx?)):\d+(?::\d+)?/g)) {
|
|
330
|
+
allPaths.add(m[1]);
|
|
331
|
+
}
|
|
332
|
+
// Form B: ESM error header "file:///path/file.js:N"
|
|
333
|
+
for (const m of stderrBuf.matchAll(/file:\/\/(\/[^\s:'"]+\.(?:m?js|cjs|jsx?|tsx?)):?\d*/g)) {
|
|
334
|
+
allPaths.add(m[1]);
|
|
335
|
+
}
|
|
336
|
+
// Form C: bare absolute path at start of line (Node prints this for CJS syntax)
|
|
337
|
+
for (const m of stderrBuf.matchAll(/^(\/[^\s:'"]+\.(?:m?js|cjs|jsx?|tsx?))(?::\d+)?$/gm)) {
|
|
338
|
+
allPaths.add(m[1]);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// First filter: paths that look like they live in this project
|
|
342
|
+
let projectFiles = [...allPaths]
|
|
343
|
+
.filter(p => p.startsWith(projectDir) || p.includes('/' + path.basename(projectDir) + '/'))
|
|
344
|
+
.map(p => p.startsWith(projectDir) ? path.relative(projectDir, p) : null)
|
|
345
|
+
.filter(Boolean)
|
|
300
346
|
.filter((v, i, a) => a.indexOf(v) === i)
|
|
301
|
-
.slice(0,
|
|
347
|
+
.slice(0, 5);
|
|
302
348
|
|
|
303
|
-
if
|
|
304
|
-
|
|
349
|
+
// FALLBACK: if no stack-trace files matched, scan the project directory
|
|
350
|
+
// for all .js / .mjs / .cjs files (excluding node_modules) and pick the
|
|
351
|
+
// ones that contain `require(` or `import` — those are the candidates.
|
|
352
|
+
if (projectFiles.length === 0) {
|
|
353
|
+
emit({ type: 'status', msg: `Stack trace did not reveal a file in project — scanning project files...` });
|
|
354
|
+
try {
|
|
355
|
+
const _walk = (dir, out = []) => {
|
|
356
|
+
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
357
|
+
if (ent.name === 'node_modules' || ent.name.startsWith('.')) continue;
|
|
358
|
+
const abs = path.join(dir, ent.name);
|
|
359
|
+
if (ent.isDirectory()) _walk(abs, out);
|
|
360
|
+
else if (/\.(m?js|cjs)$/.test(ent.name)) out.push(abs);
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
};
|
|
364
|
+
const allJs = _walk(projectDir);
|
|
365
|
+
// Heuristic — pick files matching the error semantics
|
|
366
|
+
const triggers = isRequireError
|
|
367
|
+
? /\brequire\s*\(/
|
|
368
|
+
: isImportError ? /^\s*import\s+/m
|
|
369
|
+
: /\brequire\s*\(|^\s*import\s+/m;
|
|
370
|
+
const candidates = allJs
|
|
371
|
+
.filter(abs => {
|
|
372
|
+
try { return triggers.test(fs.readFileSync(abs, 'utf-8')); } catch { return false; }
|
|
373
|
+
})
|
|
374
|
+
.slice(0, 3);
|
|
375
|
+
projectFiles = candidates.map(abs => path.relative(projectDir, abs));
|
|
376
|
+
} catch (e) {
|
|
377
|
+
emit({ type: 'warn', msg: `Project scan failed: ${e.message.slice(0, 200)}` });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (projectFiles.length === 0) {
|
|
382
|
+
emit({ type: 'warn', msg: `Auto-fix: could not identify a target file to repair. Stack trace shown above.` });
|
|
383
|
+
} else {
|
|
384
|
+
emit({ type: 'phase', phase: 'autofix', msg: `Auto-fix repairing ${projectFiles.length} file(s): ${projectFiles.join(', ')}` });
|
|
305
385
|
let anyFixed = false;
|
|
306
386
|
for (const rel of projectFiles) {
|
|
307
387
|
const abs = path.join(projectDir, rel);
|
|
@@ -309,8 +389,6 @@ class SandboxManager {
|
|
|
309
389
|
try { original = fs.readFileSync(abs, 'utf-8'); } catch { continue; }
|
|
310
390
|
if (!original) continue;
|
|
311
391
|
|
|
312
|
-
// Build a focused repair prompt — include the runtime error so the
|
|
313
|
-
// model knows exactly what to fix.
|
|
314
392
|
const errSnippet = stderrBuf.split('\n').slice(0, 30).join('\n');
|
|
315
393
|
const fixPrompt =
|
|
316
394
|
`A Node.js sandbox crashed with this runtime error:
|
|
@@ -327,40 +405,26 @@ Current file content:
|
|
|
327
405
|
${original.slice(0, 12_000)}`;
|
|
328
406
|
|
|
329
407
|
try {
|
|
408
|
+
emit({ type: 'status', msg: `Auto-fix: rewriting ${rel}...` });
|
|
330
409
|
let fixed = '';
|
|
331
410
|
await callLLMStream(loadConfig(), 'You are a precise code repair assistant. Output only the corrected file, no explanation.', fixPrompt, (c) => { fixed += c; }, { max_tokens: 16384 });
|
|
332
411
|
fixed = fixed.replace(/^```[\w]*\n/, '').replace(/\n```$/, '').trim();
|
|
333
412
|
if (fixed.length > 20 && fixed !== original) {
|
|
334
413
|
fs.writeFileSync(abs, fixed, 'utf-8');
|
|
335
|
-
emit({ type: 'status', msg: `
|
|
414
|
+
emit({ type: 'status', msg: `Auto-fix: ✓ repaired ${rel} (${fixed.length} chars)` });
|
|
336
415
|
anyFixed = true;
|
|
416
|
+
} else {
|
|
417
|
+
emit({ type: 'warn', msg: `Auto-fix: LLM returned no useful change for ${rel}` });
|
|
337
418
|
}
|
|
338
419
|
} catch (e) {
|
|
339
|
-
emit({ type: 'warn', msg: `LLM repair of ${rel} failed
|
|
420
|
+
emit({ type: 'warn', msg: `Auto-fix: LLM repair of ${rel} failed — ${(e.message || '').slice(0, 200)}` });
|
|
340
421
|
}
|
|
341
422
|
}
|
|
342
423
|
if (anyFixed) {
|
|
343
|
-
emit({ type: 'status', msg: `
|
|
424
|
+
emit({ type: 'status', msg: `Auto-fix: restarting sandbox (attempt ${_attempt + 1}/${MAX_RETRIES})...` });
|
|
344
425
|
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
345
426
|
}
|
|
346
427
|
}
|
|
347
|
-
|
|
348
|
-
// Special fallback for CJS/ESM mismatches: try flipping package.json "type"
|
|
349
|
-
if (matchedPattern.name.includes('CJS/ESM') || matchedPattern.name.includes('ESM/CJS')) {
|
|
350
|
-
const pkgPath = path.join(projectDir, 'package.json');
|
|
351
|
-
if (fs.existsSync(pkgPath)) {
|
|
352
|
-
try {
|
|
353
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
354
|
-
const isCjsError = /Cannot use import statement outside a module/i.test(stderrBuf);
|
|
355
|
-
if (isCjsError && pkg.type !== 'module') {
|
|
356
|
-
pkg.type = 'module';
|
|
357
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
358
|
-
emit({ type: 'status', msg: 'Added "type":"module" to package.json — retrying...' });
|
|
359
|
-
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
360
|
-
}
|
|
361
|
-
} catch { /* ignore */ }
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
428
|
}
|
|
365
429
|
|
|
366
430
|
// Show the actual error from stderr — include full trace for debugging
|
|
@@ -2774,22 +2838,138 @@ export function register(router) {
|
|
|
2774
2838
|
|
|
2775
2839
|
// ── Sandbox runtime errors (reported by injected script in iframe) ────────
|
|
2776
2840
|
const sandboxErrors = [];
|
|
2841
|
+
// Debounce: only autofix the same source URL once every 8s to avoid loops
|
|
2842
|
+
const _autofixCooldown = new Map();
|
|
2843
|
+
|
|
2844
|
+
// Browser-side runtime error patterns that we know how to fix.
|
|
2845
|
+
// Each one maps to an LLM repair hint specific to the failure mode.
|
|
2846
|
+
const BROWSER_FIX_PATTERNS = [
|
|
2847
|
+
{ name: 'require is not defined', re: /require is not defined/i,
|
|
2848
|
+
hint: 'The file uses CommonJS `require()` in a browser context where it does NOT exist. Convert all `require("X")` to ES module `import X from "X"` (or named imports as appropriate). Convert `module.exports = ...` to `export default ...`. Ensure the HTML loads the script with `<script type="module" src="...">`. Keep all logic identical.' },
|
|
2849
|
+
{ name: 'module is not defined', re: /\bmodule is not defined/i,
|
|
2850
|
+
hint: 'The file references the CommonJS `module` global which does not exist in browsers. Replace `module.exports = X` with `export default X` (or `export { X }`).' },
|
|
2851
|
+
{ name: 'exports is not defined', re: /\bexports is not defined/i,
|
|
2852
|
+
hint: 'The file uses CommonJS `exports` in a browser context. Replace `exports.X = Y` with `export const X = Y`.' },
|
|
2853
|
+
{ name: 'process is not defined', re: /\bprocess is not defined/i,
|
|
2854
|
+
hint: 'The code references Node.js `process` global in the browser. Either remove the reference (e.g. delete `process.env.X` checks) or stub it: `const process = { env: {} };` at top of file. Prefer removal.' },
|
|
2855
|
+
{ name: 'SyntaxError', re: /SyntaxError:.+|Unexpected (token|identifier|string|end)/i,
|
|
2856
|
+
hint: 'JavaScript syntax error in the file. Fix the syntax — unclosed brackets, missing commas, invalid token. Output the complete corrected file.' },
|
|
2857
|
+
{ name: 'ReferenceError', re: /(?:Uncaught )?ReferenceError: (\w+) is not defined/i,
|
|
2858
|
+
hint: 'A variable is referenced but never declared/imported. Either import it from the correct module, define it, or remove the dead reference.' },
|
|
2859
|
+
{ name: 'TypeError null/undefined', re: /(?:Uncaught )?TypeError: (?:Cannot read prop(?:erties|erty)? .+ of (?:undefined|null)|null is not an object|undefined is not (?:a function|an object))/i,
|
|
2860
|
+
hint: 'Null/undefined access. Add a null-check or optional chaining (?.) before the failing access. If the failure is on DOM element lookup, the script may be running before the DOM is ready — wrap in DOMContentLoaded.' },
|
|
2861
|
+
];
|
|
2862
|
+
|
|
2777
2863
|
router.post('/api/studio/webcraft/sandbox/errors', async (req, res) => {
|
|
2778
2864
|
try {
|
|
2779
2865
|
const body = await parseBody(req);
|
|
2780
|
-
if (body.error) {
|
|
2866
|
+
if (!body.error) return sendJSON(res, 200, { ok: true });
|
|
2867
|
+
|
|
2868
|
+
const message = String(body.error).slice(0, 500);
|
|
2869
|
+
const source = String(body.source || '').slice(0, 200);
|
|
2870
|
+
const stack = String(body.stack || '').slice(0, 1000);
|
|
2871
|
+
|
|
2872
|
+
sandboxErrors.push({
|
|
2873
|
+
ts: new Date().toISOString(),
|
|
2874
|
+
message,
|
|
2875
|
+
source,
|
|
2876
|
+
line: body.line || 0,
|
|
2877
|
+
col: body.col || 0,
|
|
2878
|
+
stack,
|
|
2879
|
+
});
|
|
2880
|
+
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2881
|
+
|
|
2882
|
+
// Respond to the iframe immediately — autofix runs async after.
|
|
2883
|
+
sendJSON(res, 200, { ok: true });
|
|
2884
|
+
|
|
2885
|
+
// ── Autofix path ────────────────────────────────────────────────
|
|
2886
|
+
const sb = sandbox._sandbox;
|
|
2887
|
+
if (!sb || !sb.projectName) return; // no project — can't fix
|
|
2888
|
+
const projectDir = ProjectStore.dir(sb.projectName);
|
|
2889
|
+
if (!fs.existsSync(projectDir)) return;
|
|
2890
|
+
|
|
2891
|
+
const matched = BROWSER_FIX_PATTERNS.find(p => p.re.test(message) || p.re.test(stack));
|
|
2892
|
+
if (!matched) return;
|
|
2893
|
+
|
|
2894
|
+
// Resolve the failing file from the source URL.
|
|
2895
|
+
// source is typically "http://localhost:NNNN/path/to/file.js?..." or
|
|
2896
|
+
// a bare filename. We strip the origin and query, then map under projectDir.
|
|
2897
|
+
let relFile = '';
|
|
2898
|
+
try {
|
|
2899
|
+
if (source && source.includes('://')) {
|
|
2900
|
+
const u = new URL(source);
|
|
2901
|
+
relFile = u.pathname.replace(/^\/+/, '').split('?')[0];
|
|
2902
|
+
} else if (source) {
|
|
2903
|
+
relFile = source.replace(/^\/+/, '').split('?')[0];
|
|
2904
|
+
}
|
|
2905
|
+
// Fallback: extract from stack trace
|
|
2906
|
+
if (!relFile && stack) {
|
|
2907
|
+
const m = stack.match(/(?:https?:\/\/[^\s)]+\/)([^\s:?)]+\.(?:m?js|jsx?|tsx?|cjs|html))/);
|
|
2908
|
+
if (m) relFile = m[1];
|
|
2909
|
+
}
|
|
2910
|
+
} catch { /* ignore parse errors */ }
|
|
2911
|
+
|
|
2912
|
+
// Strip leading "static/" or asset prefixes that Vite/Webpack add
|
|
2913
|
+
relFile = relFile.replace(/^(static|assets|dist|public|build|out)\//, '');
|
|
2914
|
+
|
|
2915
|
+
if (!relFile) return;
|
|
2916
|
+
|
|
2917
|
+
const absFile = path.join(projectDir, relFile);
|
|
2918
|
+
// Safety: must be inside projectDir
|
|
2919
|
+
if (!absFile.startsWith(projectDir + path.sep)) return;
|
|
2920
|
+
if (!fs.existsSync(absFile)) return;
|
|
2921
|
+
|
|
2922
|
+
// Cooldown: don't autofix the same file more than once every 8s
|
|
2923
|
+
const cdKey = absFile;
|
|
2924
|
+
const now = Date.now();
|
|
2925
|
+
const last = _autofixCooldown.get(cdKey) || 0;
|
|
2926
|
+
if (now - last < 8_000) return;
|
|
2927
|
+
_autofixCooldown.set(cdKey, now);
|
|
2928
|
+
|
|
2929
|
+
const original = fs.readFileSync(absFile, 'utf-8');
|
|
2930
|
+
if (!original || original.length < 5) return;
|
|
2931
|
+
|
|
2932
|
+
const fixPrompt =
|
|
2933
|
+
`A browser sandbox preview crashed with this runtime error:
|
|
2934
|
+
|
|
2935
|
+
Error: ${message}
|
|
2936
|
+
Source: ${source}
|
|
2937
|
+
Stack (first lines):
|
|
2938
|
+
${stack.slice(0, 800)}
|
|
2939
|
+
|
|
2940
|
+
The failing file is: ${relFile}
|
|
2941
|
+
|
|
2942
|
+
What to fix: ${matched.hint}
|
|
2943
|
+
|
|
2944
|
+
CRITICAL: Output ONLY the complete corrected file content. No commentary, no markdown fences. Keep all working logic intact — only change what's necessary to fix the runtime error.
|
|
2945
|
+
|
|
2946
|
+
Current file content:
|
|
2947
|
+
${original.slice(0, 12_000)}`;
|
|
2948
|
+
|
|
2949
|
+
try {
|
|
2950
|
+
let fixed = '';
|
|
2951
|
+
await callLLMStream(loadConfig(), 'You are a precise code repair assistant. Output only the corrected file, no explanation.', fixPrompt, (c) => { fixed += c; }, { max_tokens: 16_384 });
|
|
2952
|
+
fixed = fixed.replace(/^```[\w]*\n/, '').replace(/\n```$/, '').trim();
|
|
2953
|
+
if (fixed.length > 20 && fixed !== original) {
|
|
2954
|
+
fs.writeFileSync(absFile, fixed, 'utf-8');
|
|
2955
|
+
// Notify connected WebSocket clients so the iframe can be reloaded.
|
|
2956
|
+
// The Studio/WebCraft UI polls these errors and will surface the fix.
|
|
2957
|
+
sandboxErrors.push({
|
|
2958
|
+
ts: new Date().toISOString(),
|
|
2959
|
+
message: `✓ Auto-fix applied to ${relFile} (${matched.name}) — reload the preview to test.`,
|
|
2960
|
+
source: '__autofix__',
|
|
2961
|
+
line: 0, col: 0, stack: '',
|
|
2962
|
+
});
|
|
2963
|
+
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2964
|
+
}
|
|
2965
|
+
} catch (e) {
|
|
2781
2966
|
sandboxErrors.push({
|
|
2782
2967
|
ts: new Date().toISOString(),
|
|
2783
|
-
message:
|
|
2784
|
-
source:
|
|
2785
|
-
line:
|
|
2786
|
-
col: body.col || 0,
|
|
2787
|
-
stack: String(body.stack || '').slice(0, 1000),
|
|
2968
|
+
message: `✗ Auto-fix failed for ${relFile}: ${(e.message || '').slice(0, 200)}`,
|
|
2969
|
+
source: '__autofix__',
|
|
2970
|
+
line: 0, col: 0, stack: '',
|
|
2788
2971
|
});
|
|
2789
|
-
// Keep only last 20 errors
|
|
2790
|
-
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2791
2972
|
}
|
|
2792
|
-
sendJSON(res, 200, { ok: true });
|
|
2793
2973
|
} catch { sendJSON(res, 200, { ok: true }); }
|
|
2794
2974
|
});
|
|
2795
2975
|
|