nothumanallowed 15.1.17 → 15.1.19
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 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.19",
|
|
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.19';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -246,11 +246,10 @@ class SandboxManager {
|
|
|
246
246
|
if (this._stoppedByUser) return;
|
|
247
247
|
emit({ type: 'status', msg: `Process exited with code ${exitCode}` });
|
|
248
248
|
|
|
249
|
-
//
|
|
249
|
+
// ── Tier 1: missing module → npm install + retry ─────────────────────
|
|
250
250
|
const missingMatch = stderrBuf.match(/Cannot find module ['"]([^'"]+)['"]/);
|
|
251
251
|
if (missingMatch && _attempt < MAX_RETRIES) {
|
|
252
252
|
const missingMod = missingMatch[1];
|
|
253
|
-
// Skip shim-able or built-in modules
|
|
254
253
|
if (!missingMod.startsWith('.') && !missingMod.startsWith('/') && !missingMod.startsWith('node:')) {
|
|
255
254
|
const pkgName = missingMod.startsWith('@') ? missingMod.split('/').slice(0, 2).join('/') : missingMod.split('/')[0];
|
|
256
255
|
emit({ type: 'phase', phase: 'autofix', msg: `Missing module "${pkgName}" — installing...` });
|
|
@@ -268,6 +267,102 @@ class SandboxManager {
|
|
|
268
267
|
}
|
|
269
268
|
}
|
|
270
269
|
|
|
270
|
+
// ── Tier 2: runtime errors that need code fix (require/import mismatch,
|
|
271
|
+
// SyntaxError, ReferenceError, TypeError) — extract failing file from
|
|
272
|
+
// stack trace and ask LLM to repair it. This is the bug the user hit:
|
|
273
|
+
// "require is not defined" inside an ESM project should auto-rewrite
|
|
274
|
+
// require() → import statements, or flip package.json "type" field. ───
|
|
275
|
+
const runtimePatterns = [
|
|
276
|
+
{ name: 'CJS/ESM mismatch', re: /require is not defined/i,
|
|
277
|
+
hint: 'The file uses CommonJS `require()` but the project is ESM ("type":"module" in package.json or .mjs extension). Convert all `require(\'X\')` to ES module `import` statements. Convert `module.exports = ...` to `export default ...`. Keep all logic identical.' },
|
|
278
|
+
{ name: 'ESM/CJS mismatch', re: /Cannot use import statement outside a module/i,
|
|
279
|
+
hint: 'The file uses ES module `import` but the project is CommonJS. Either add `"type":"module"` to package.json (preferred for new projects) or convert imports to `require()`.' },
|
|
280
|
+
{ name: 'import.meta misuse', re: /import\.meta(?:\.url)? is only valid in/i,
|
|
281
|
+
hint: 'The file uses `import.meta` in a CommonJS context. Either switch the project to ESM (add `"type":"module"` to package.json) or replace `import.meta.url` with `__filename` / `__dirname`.' },
|
|
282
|
+
{ name: 'SyntaxError', re: /SyntaxError:.+/i,
|
|
283
|
+
hint: 'The file has a JavaScript syntax error. Fix the syntax issue — unclosed brackets, missing commas, invalid tokens. Output the complete corrected file.' },
|
|
284
|
+
{ name: 'ReferenceError', re: /ReferenceError: (\w+) is not defined/i,
|
|
285
|
+
hint: 'A variable is referenced but never declared/imported. Either import it from the correct module or declare it. Common missing globals: "require" (use import), "process" (Node only — add `import process from \'node:process\'` in ESM), "__dirname" (in ESM use `import.meta.url` + fileURLToPath).' },
|
|
286
|
+
{ name: 'TypeError null/undefined', re: /TypeError: Cannot read prop(?:erties|erty)? .+ of (?:undefined|null)/i,
|
|
287
|
+
hint: 'Null/undefined access. Add a null-check or optional chaining (?.) before the failing access.' },
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
const matchedPattern = runtimePatterns.find(p => p.re.test(stderrBuf));
|
|
291
|
+
if (matchedPattern && _attempt < MAX_RETRIES) {
|
|
292
|
+
// Extract the file path from the stack trace.
|
|
293
|
+
// Patterns: "at /abs/path/file.js:N:M", "at file:///abs/path/file.js:N:M",
|
|
294
|
+
// "at Object.<anonymous> (/path/file.js:N:M)", or simply at start of stderr.
|
|
295
|
+
const fileMatches = [...stderrBuf.matchAll(/(?:at\s+(?:[\w<>\.\s]+\s+)?\(?)?(?:file:\/\/)?(\/[^\s:()'"]+\.(?:m?js|cjs|jsx?|tsx?)):\d+(?::\d+)?/g)];
|
|
296
|
+
const projectFiles = fileMatches
|
|
297
|
+
.map(m => m[1])
|
|
298
|
+
.filter(p => p && p.startsWith(projectDir))
|
|
299
|
+
.map(p => path.relative(projectDir, p))
|
|
300
|
+
.filter((v, i, a) => a.indexOf(v) === i)
|
|
301
|
+
.slice(0, 3);
|
|
302
|
+
|
|
303
|
+
if (projectFiles.length > 0) {
|
|
304
|
+
emit({ type: 'phase', phase: 'autofix', msg: `Runtime error (${matchedPattern.name}) — repairing ${projectFiles.length} file(s)...` });
|
|
305
|
+
let anyFixed = false;
|
|
306
|
+
for (const rel of projectFiles) {
|
|
307
|
+
const abs = path.join(projectDir, rel);
|
|
308
|
+
let original = '';
|
|
309
|
+
try { original = fs.readFileSync(abs, 'utf-8'); } catch { continue; }
|
|
310
|
+
if (!original) continue;
|
|
311
|
+
|
|
312
|
+
// Build a focused repair prompt — include the runtime error so the
|
|
313
|
+
// model knows exactly what to fix.
|
|
314
|
+
const errSnippet = stderrBuf.split('\n').slice(0, 30).join('\n');
|
|
315
|
+
const fixPrompt =
|
|
316
|
+
`A Node.js sandbox crashed with this runtime error:
|
|
317
|
+
|
|
318
|
+
${errSnippet.slice(0, 1500)}
|
|
319
|
+
|
|
320
|
+
The failing file is: ${rel}
|
|
321
|
+
|
|
322
|
+
What to fix: ${matchedPattern.hint}
|
|
323
|
+
|
|
324
|
+
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 error.
|
|
325
|
+
|
|
326
|
+
Current file content:
|
|
327
|
+
${original.slice(0, 12_000)}`;
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
let fixed = '';
|
|
331
|
+
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
|
+
fixed = fixed.replace(/^```[\w]*\n/, '').replace(/\n```$/, '').trim();
|
|
333
|
+
if (fixed.length > 20 && fixed !== original) {
|
|
334
|
+
fs.writeFileSync(abs, fixed, 'utf-8');
|
|
335
|
+
emit({ type: 'status', msg: `Repaired ${rel} (${fixed.length} chars)` });
|
|
336
|
+
anyFixed = true;
|
|
337
|
+
}
|
|
338
|
+
} catch (e) {
|
|
339
|
+
emit({ type: 'warn', msg: `LLM repair of ${rel} failed: ${(e.message || '').slice(0, 200)}` });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (anyFixed) {
|
|
343
|
+
emit({ type: 'status', msg: `Restarting sandbox (attempt ${_attempt + 1}/${MAX_RETRIES})...` });
|
|
344
|
+
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
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
|
+
}
|
|
365
|
+
|
|
271
366
|
// Show the actual error from stderr — include full trace for debugging
|
|
272
367
|
const stderrLines = stderrBuf.split('\n').filter(l => l.trim());
|
|
273
368
|
const errLine = stderrLines.find((l) => l.includes('Error') || l.includes('error'));
|
|
@@ -2679,22 +2774,138 @@ export function register(router) {
|
|
|
2679
2774
|
|
|
2680
2775
|
// ── Sandbox runtime errors (reported by injected script in iframe) ────────
|
|
2681
2776
|
const sandboxErrors = [];
|
|
2777
|
+
// Debounce: only autofix the same source URL once every 8s to avoid loops
|
|
2778
|
+
const _autofixCooldown = new Map();
|
|
2779
|
+
|
|
2780
|
+
// Browser-side runtime error patterns that we know how to fix.
|
|
2781
|
+
// Each one maps to an LLM repair hint specific to the failure mode.
|
|
2782
|
+
const BROWSER_FIX_PATTERNS = [
|
|
2783
|
+
{ name: 'require is not defined', re: /require is not defined/i,
|
|
2784
|
+
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.' },
|
|
2785
|
+
{ name: 'module is not defined', re: /\bmodule is not defined/i,
|
|
2786
|
+
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 }`).' },
|
|
2787
|
+
{ name: 'exports is not defined', re: /\bexports is not defined/i,
|
|
2788
|
+
hint: 'The file uses CommonJS `exports` in a browser context. Replace `exports.X = Y` with `export const X = Y`.' },
|
|
2789
|
+
{ name: 'process is not defined', re: /\bprocess is not defined/i,
|
|
2790
|
+
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.' },
|
|
2791
|
+
{ name: 'SyntaxError', re: /SyntaxError:.+|Unexpected (token|identifier|string|end)/i,
|
|
2792
|
+
hint: 'JavaScript syntax error in the file. Fix the syntax — unclosed brackets, missing commas, invalid token. Output the complete corrected file.' },
|
|
2793
|
+
{ name: 'ReferenceError', re: /(?:Uncaught )?ReferenceError: (\w+) is not defined/i,
|
|
2794
|
+
hint: 'A variable is referenced but never declared/imported. Either import it from the correct module, define it, or remove the dead reference.' },
|
|
2795
|
+
{ 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,
|
|
2796
|
+
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.' },
|
|
2797
|
+
];
|
|
2798
|
+
|
|
2682
2799
|
router.post('/api/studio/webcraft/sandbox/errors', async (req, res) => {
|
|
2683
2800
|
try {
|
|
2684
2801
|
const body = await parseBody(req);
|
|
2685
|
-
if (body.error) {
|
|
2802
|
+
if (!body.error) return sendJSON(res, 200, { ok: true });
|
|
2803
|
+
|
|
2804
|
+
const message = String(body.error).slice(0, 500);
|
|
2805
|
+
const source = String(body.source || '').slice(0, 200);
|
|
2806
|
+
const stack = String(body.stack || '').slice(0, 1000);
|
|
2807
|
+
|
|
2808
|
+
sandboxErrors.push({
|
|
2809
|
+
ts: new Date().toISOString(),
|
|
2810
|
+
message,
|
|
2811
|
+
source,
|
|
2812
|
+
line: body.line || 0,
|
|
2813
|
+
col: body.col || 0,
|
|
2814
|
+
stack,
|
|
2815
|
+
});
|
|
2816
|
+
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2817
|
+
|
|
2818
|
+
// Respond to the iframe immediately — autofix runs async after.
|
|
2819
|
+
sendJSON(res, 200, { ok: true });
|
|
2820
|
+
|
|
2821
|
+
// ── Autofix path ────────────────────────────────────────────────
|
|
2822
|
+
const sb = sandbox._sandbox;
|
|
2823
|
+
if (!sb || !sb.projectName) return; // no project — can't fix
|
|
2824
|
+
const projectDir = ProjectStore.dir(sb.projectName);
|
|
2825
|
+
if (!fs.existsSync(projectDir)) return;
|
|
2826
|
+
|
|
2827
|
+
const matched = BROWSER_FIX_PATTERNS.find(p => p.re.test(message) || p.re.test(stack));
|
|
2828
|
+
if (!matched) return;
|
|
2829
|
+
|
|
2830
|
+
// Resolve the failing file from the source URL.
|
|
2831
|
+
// source is typically "http://localhost:NNNN/path/to/file.js?..." or
|
|
2832
|
+
// a bare filename. We strip the origin and query, then map under projectDir.
|
|
2833
|
+
let relFile = '';
|
|
2834
|
+
try {
|
|
2835
|
+
if (source && source.includes('://')) {
|
|
2836
|
+
const u = new URL(source);
|
|
2837
|
+
relFile = u.pathname.replace(/^\/+/, '').split('?')[0];
|
|
2838
|
+
} else if (source) {
|
|
2839
|
+
relFile = source.replace(/^\/+/, '').split('?')[0];
|
|
2840
|
+
}
|
|
2841
|
+
// Fallback: extract from stack trace
|
|
2842
|
+
if (!relFile && stack) {
|
|
2843
|
+
const m = stack.match(/(?:https?:\/\/[^\s)]+\/)([^\s:?)]+\.(?:m?js|jsx?|tsx?|cjs|html))/);
|
|
2844
|
+
if (m) relFile = m[1];
|
|
2845
|
+
}
|
|
2846
|
+
} catch { /* ignore parse errors */ }
|
|
2847
|
+
|
|
2848
|
+
// Strip leading "static/" or asset prefixes that Vite/Webpack add
|
|
2849
|
+
relFile = relFile.replace(/^(static|assets|dist|public|build|out)\//, '');
|
|
2850
|
+
|
|
2851
|
+
if (!relFile) return;
|
|
2852
|
+
|
|
2853
|
+
const absFile = path.join(projectDir, relFile);
|
|
2854
|
+
// Safety: must be inside projectDir
|
|
2855
|
+
if (!absFile.startsWith(projectDir + path.sep)) return;
|
|
2856
|
+
if (!fs.existsSync(absFile)) return;
|
|
2857
|
+
|
|
2858
|
+
// Cooldown: don't autofix the same file more than once every 8s
|
|
2859
|
+
const cdKey = absFile;
|
|
2860
|
+
const now = Date.now();
|
|
2861
|
+
const last = _autofixCooldown.get(cdKey) || 0;
|
|
2862
|
+
if (now - last < 8_000) return;
|
|
2863
|
+
_autofixCooldown.set(cdKey, now);
|
|
2864
|
+
|
|
2865
|
+
const original = fs.readFileSync(absFile, 'utf-8');
|
|
2866
|
+
if (!original || original.length < 5) return;
|
|
2867
|
+
|
|
2868
|
+
const fixPrompt =
|
|
2869
|
+
`A browser sandbox preview crashed with this runtime error:
|
|
2870
|
+
|
|
2871
|
+
Error: ${message}
|
|
2872
|
+
Source: ${source}
|
|
2873
|
+
Stack (first lines):
|
|
2874
|
+
${stack.slice(0, 800)}
|
|
2875
|
+
|
|
2876
|
+
The failing file is: ${relFile}
|
|
2877
|
+
|
|
2878
|
+
What to fix: ${matched.hint}
|
|
2879
|
+
|
|
2880
|
+
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.
|
|
2881
|
+
|
|
2882
|
+
Current file content:
|
|
2883
|
+
${original.slice(0, 12_000)}`;
|
|
2884
|
+
|
|
2885
|
+
try {
|
|
2886
|
+
let fixed = '';
|
|
2887
|
+
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 });
|
|
2888
|
+
fixed = fixed.replace(/^```[\w]*\n/, '').replace(/\n```$/, '').trim();
|
|
2889
|
+
if (fixed.length > 20 && fixed !== original) {
|
|
2890
|
+
fs.writeFileSync(absFile, fixed, 'utf-8');
|
|
2891
|
+
// Notify connected WebSocket clients so the iframe can be reloaded.
|
|
2892
|
+
// The Studio/WebCraft UI polls these errors and will surface the fix.
|
|
2893
|
+
sandboxErrors.push({
|
|
2894
|
+
ts: new Date().toISOString(),
|
|
2895
|
+
message: `✓ Auto-fix applied to ${relFile} (${matched.name}) — reload the preview to test.`,
|
|
2896
|
+
source: '__autofix__',
|
|
2897
|
+
line: 0, col: 0, stack: '',
|
|
2898
|
+
});
|
|
2899
|
+
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2900
|
+
}
|
|
2901
|
+
} catch (e) {
|
|
2686
2902
|
sandboxErrors.push({
|
|
2687
2903
|
ts: new Date().toISOString(),
|
|
2688
|
-
message:
|
|
2689
|
-
source:
|
|
2690
|
-
line:
|
|
2691
|
-
col: body.col || 0,
|
|
2692
|
-
stack: String(body.stack || '').slice(0, 1000),
|
|
2904
|
+
message: `✗ Auto-fix failed for ${relFile}: ${(e.message || '').slice(0, 200)}`,
|
|
2905
|
+
source: '__autofix__',
|
|
2906
|
+
line: 0, col: 0, stack: '',
|
|
2693
2907
|
});
|
|
2694
|
-
// Keep only last 20 errors
|
|
2695
|
-
if (sandboxErrors.length > 20) sandboxErrors.splice(0, sandboxErrors.length - 20);
|
|
2696
2908
|
}
|
|
2697
|
-
sendJSON(res, 200, { ok: true });
|
|
2698
2909
|
} catch { sendJSON(res, 200, { ok: true }); }
|
|
2699
2910
|
});
|
|
2700
2911
|
|