nothumanallowed 14.1.14 → 14.1.15
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": "14.1.
|
|
3
|
+
"version": "14.1.15",
|
|
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 = '14.1.
|
|
8
|
+
export const VERSION = '14.1.15';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -526,13 +526,24 @@ RULES:
|
|
|
526
526
|
|
|
527
527
|
// ── Generation pipeline (SSE) ─────────────────────────────────────────────────
|
|
528
528
|
|
|
529
|
-
const FILE_PLAN_SYSTEM = `You are
|
|
530
|
-
Output ONLY a JSON array
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
const FILE_PLAN_SYSTEM = `You are a senior full-stack architect. Design a COMPLETE, PRODUCTION-READY file structure for a web project.
|
|
530
|
+
Output ONLY a JSON array: [{"name":"path/to/file.ext","purpose":"what this file does","tokens":N}]
|
|
531
|
+
where "tokens" is your estimate of how many tokens the file content will need (200-800 for small files, 800-2000 for medium, 2000-4000 for large).
|
|
532
|
+
|
|
533
|
+
MANDATORY rules:
|
|
534
|
+
- Generate 20-40 files minimum for any real project — a complete site requires many files
|
|
535
|
+
- Split large concerns into separate files (separate route files, separate component files, separate util files)
|
|
536
|
+
- Always include: package.json, server.js (or index.js), .env.example, README.md
|
|
537
|
+
- For full-stack projects: routes/, middleware/, models/, controllers/ directories with individual files per resource
|
|
538
|
+
- For frontend: separate CSS files per section (hero, navbar, footer, components), separate JS modules
|
|
539
|
+
- Use relative paths only (e.g. "routes/auth.js", "public/js/app.js", "public/css/main.css")
|
|
534
540
|
- No explanation, no markdown, ONLY the JSON array.`;
|
|
535
541
|
|
|
542
|
+
// Token counter — approximate based on character count (1 token ≈ 4 chars)
|
|
543
|
+
function countTokens(text) {
|
|
544
|
+
return Math.ceil((text || '').length / 4);
|
|
545
|
+
}
|
|
546
|
+
|
|
536
547
|
async function runGenerate(config, projectName, description, blocks, authFields, emit) {
|
|
537
548
|
const blocksDesc = Object.entries(blocks)
|
|
538
549
|
.filter(([, enabled]) => enabled)
|
|
@@ -546,12 +557,16 @@ async function runGenerate(config, projectName, description, blocks, authFields,
|
|
|
546
557
|
Description: ${description}
|
|
547
558
|
${blocksDesc ? `Required blocks: ${blocksDesc}` : ''}
|
|
548
559
|
${authDesc}
|
|
549
|
-
|
|
560
|
+
|
|
561
|
+
Design a COMPLETE production-ready file structure. Include ALL files needed for a fully working site: server, routes, middleware, models, public HTML/CSS/JS pages, config files, README. Minimum 20 files.`;
|
|
550
562
|
|
|
551
563
|
// Round 1: plan files
|
|
552
564
|
let filePlan = [];
|
|
565
|
+
let planTokensIn = countTokens(FILE_PLAN_SYSTEM) + countTokens(planPrompt);
|
|
566
|
+
let planTokensOut = 0;
|
|
553
567
|
try {
|
|
554
|
-
const planRaw = await callLLM(config, FILE_PLAN_SYSTEM, planPrompt, { max_tokens:
|
|
568
|
+
const planRaw = await callLLM(config, FILE_PLAN_SYSTEM, planPrompt, { max_tokens: 4096 });
|
|
569
|
+
planTokensOut = countTokens(planRaw);
|
|
555
570
|
const clean = planRaw.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
|
|
556
571
|
.replace(/^```[\w]*\n?/, '').replace(/\n?```$/, '').trim();
|
|
557
572
|
const arr = JSON.parse(clean.match(/\[[\s\S]*\]/)?.[0] ?? clean);
|
|
@@ -559,13 +574,34 @@ Design a complete file structure for this project.`;
|
|
|
559
574
|
} catch {}
|
|
560
575
|
|
|
561
576
|
if (filePlan.length === 0) {
|
|
562
|
-
//
|
|
577
|
+
// Comprehensive fallback structure for a full-stack web project
|
|
563
578
|
filePlan = [
|
|
564
|
-
{ name: '
|
|
565
|
-
{ name: '
|
|
566
|
-
{ name: '
|
|
567
|
-
{ name: 'server.js', purpose: 'Express server' },
|
|
568
|
-
{ name: '
|
|
579
|
+
{ name: 'package.json', purpose: 'Node.js dependencies and scripts', tokens: 300 },
|
|
580
|
+
{ name: '.env.example', purpose: 'Environment variables template', tokens: 200 },
|
|
581
|
+
{ name: 'README.md', purpose: 'Project documentation', tokens: 400 },
|
|
582
|
+
{ name: 'server.js', purpose: 'Express server entry point with middleware setup', tokens: 1500 },
|
|
583
|
+
{ name: 'routes/index.js', purpose: 'Main router — mounts all sub-routers', tokens: 300 },
|
|
584
|
+
{ name: 'routes/auth.js', purpose: 'Auth routes: register, login, logout, refresh', tokens: 1200 },
|
|
585
|
+
{ name: 'routes/api.js', purpose: 'REST API routes', tokens: 800 },
|
|
586
|
+
{ name: 'middleware/auth.js', purpose: 'JWT authentication middleware', tokens: 600 },
|
|
587
|
+
{ name: 'middleware/error.js', purpose: 'Global error handler', tokens: 400 },
|
|
588
|
+
{ name: 'middleware/validate.js', purpose: 'Request validation middleware', tokens: 500 },
|
|
589
|
+
{ name: 'models/user.js', purpose: 'User model (SQLite/JSON storage)', tokens: 600 },
|
|
590
|
+
{ name: 'controllers/authController.js', purpose: 'Auth business logic', tokens: 1200 },
|
|
591
|
+
{ name: 'utils/jwt.js', purpose: 'JWT sign/verify helpers', tokens: 400 },
|
|
592
|
+
{ name: 'utils/hash.js', purpose: 'Password hashing utilities (bcrypt)', tokens: 300 },
|
|
593
|
+
{ name: 'config/database.js', purpose: 'Database connection and setup', tokens: 500 },
|
|
594
|
+
{ name: 'public/index.html', purpose: 'Main landing page', tokens: 2000 },
|
|
595
|
+
{ name: 'public/dashboard.html', purpose: 'User dashboard page', tokens: 1500 },
|
|
596
|
+
{ name: 'public/login.html', purpose: 'Login/register page', tokens: 1200 },
|
|
597
|
+
{ name: 'public/css/main.css', purpose: 'Global styles, variables, reset', tokens: 1500 },
|
|
598
|
+
{ name: 'public/css/components.css', purpose: 'Reusable component styles (cards, buttons, forms)', tokens: 1200 },
|
|
599
|
+
{ name: 'public/css/layout.css', purpose: 'Layout styles: nav, hero, sections, footer', tokens: 1000 },
|
|
600
|
+
{ name: 'public/css/animations.css', purpose: 'Animations and transitions', tokens: 600 },
|
|
601
|
+
{ name: 'public/js/app.js', purpose: 'Main frontend JS — router, init', tokens: 800 },
|
|
602
|
+
{ name: 'public/js/auth.js', purpose: 'Frontend auth logic — login, register, token storage', tokens: 800 },
|
|
603
|
+
{ name: 'public/js/api.js', purpose: 'API client wrapper with fetch', tokens: 500 },
|
|
604
|
+
{ name: 'public/js/ui.js', purpose: 'UI helpers — toasts, modals, loaders', tokens: 600 },
|
|
569
605
|
];
|
|
570
606
|
}
|
|
571
607
|
|
|
@@ -573,43 +609,70 @@ Design a complete file structure for this project.`;
|
|
|
573
609
|
|
|
574
610
|
const projectDir = ensureDir(ProjectStore.dir(projectName));
|
|
575
611
|
const generatedFiles = [];
|
|
612
|
+
let totalTokensIn = planTokensIn;
|
|
613
|
+
let totalTokensOut = planTokensOut;
|
|
576
614
|
|
|
577
|
-
|
|
615
|
+
const allFileNames = filePlan.map((f) => f.name).join(', ');
|
|
616
|
+
|
|
617
|
+
// Round 2: generate each file with streaming
|
|
578
618
|
for (let fi = 0; fi < filePlan.length; fi++) {
|
|
579
619
|
const fileSpec = filePlan[fi];
|
|
580
620
|
emit({ type: 'file_start', name: fileSpec.name, fi: fi + 1, total: filePlan.length });
|
|
581
621
|
|
|
582
|
-
|
|
583
|
-
const prevContext = generatedFiles.slice(-
|
|
584
|
-
.map((f) =>
|
|
622
|
+
// Include last 4 generated files as context (truncated to avoid token overflow)
|
|
623
|
+
const prevContext = generatedFiles.slice(-4)
|
|
624
|
+
.map((f) => {
|
|
625
|
+
const ext = f.name.split('.').pop();
|
|
626
|
+
const snippet = f.content.slice(0, ext === 'json' ? 600 : 1200);
|
|
627
|
+
return `### ${f.name}\n\`\`\`\n${snippet}${f.content.length > 1200 ? '\n... (truncated)' : ''}\n\`\`\``;
|
|
628
|
+
})
|
|
585
629
|
.join('\n\n');
|
|
586
630
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
631
|
+
// Estimate appropriate max_tokens for this file
|
|
632
|
+
const estimatedTokens = fileSpec.tokens || 2000;
|
|
633
|
+
const maxTokens = Math.min(Math.max(estimatedTokens * 2, 2000), 8192);
|
|
634
|
+
|
|
635
|
+
const fileSys = `You are a senior full-stack developer generating a COMPLETE, PRODUCTION-READY file.
|
|
636
|
+
CRITICAL RULES:
|
|
637
|
+
- Output ONLY the raw file content — zero explanations, zero markdown fences, zero "here is the file:" preamble
|
|
638
|
+
- Write COMPLETE, WORKING code — no TODOs, no placeholders, no "add your code here" comments
|
|
639
|
+
- Every function must be fully implemented with real logic
|
|
640
|
+
- Use modern patterns: async/await, ES6+, proper error handling
|
|
641
|
+
- CSS must include responsive design (mobile-first), dark/light variables, smooth animations
|
|
642
|
+
- HTML must be complete with proper meta tags, semantic structure, accessible markup
|
|
643
|
+
- JS must handle all edge cases, show loading states, handle errors gracefully`;
|
|
590
644
|
|
|
591
645
|
const filePrompt = `Project: ${projectName}
|
|
592
646
|
Description: ${description}
|
|
593
|
-
${blocksDesc ? `
|
|
647
|
+
${blocksDesc ? `Enabled blocks: ${blocksDesc}` : ''}
|
|
594
648
|
${authDesc}
|
|
595
|
-
|
|
649
|
+
Full project file list: ${allFileNames}
|
|
596
650
|
|
|
597
|
-
|
|
651
|
+
NOW GENERATE: ${fileSpec.name}
|
|
598
652
|
Purpose: ${fileSpec.purpose}
|
|
599
|
-
${prevContext ? `\n--- Recent files for context ---\n${prevContext}` : ''}
|
|
600
653
|
|
|
601
|
-
Output ONLY the complete file content.`;
|
|
654
|
+
${prevContext ? `Recent files generated (for consistency):\n${prevContext}\n\n` : ''}Output ONLY the complete file content, starting immediately with the first line of the file.`;
|
|
602
655
|
|
|
603
656
|
let fileContent = '';
|
|
604
657
|
let syntaxError = null;
|
|
658
|
+
const fileTokensIn = countTokens(fileSys) + countTokens(filePrompt);
|
|
605
659
|
|
|
606
660
|
try {
|
|
607
661
|
await callLLMStream(config, fileSys, filePrompt, (chunk) => {
|
|
608
662
|
fileContent += chunk;
|
|
609
663
|
emit({ type: 'file_chunk', name: fileSpec.name, chunk, fi: fi + 1, total: filePlan.length });
|
|
610
|
-
}, { max_tokens:
|
|
664
|
+
}, { max_tokens: maxTokens });
|
|
665
|
+
|
|
666
|
+
const fileTokensOut = countTokens(fileContent);
|
|
667
|
+
totalTokensIn += fileTokensIn;
|
|
668
|
+
totalTokensOut += fileTokensOut;
|
|
669
|
+
|
|
670
|
+
// Strip markdown fences if LLM wrapped the output anyway
|
|
671
|
+
fileContent = fileContent
|
|
672
|
+
.replace(/^```[\w]*\n/, '').replace(/\n```$/, '')
|
|
673
|
+
.replace(/^```[\w]*\r\n/, '').replace(/\r\n```$/, '').trim();
|
|
611
674
|
|
|
612
|
-
// Quick syntax check for JS files
|
|
675
|
+
// Quick syntax check for JS/TS files
|
|
613
676
|
if (fileSpec.name.endsWith('.js') || fileSpec.name.endsWith('.mjs')) {
|
|
614
677
|
try { new Function(fileContent); } catch (e) { syntaxError = e.message.replace(/\n.*/s, ''); }
|
|
615
678
|
}
|
|
@@ -618,7 +681,7 @@ Output ONLY the complete file content.`;
|
|
|
618
681
|
ensureDir(path.dirname(abs));
|
|
619
682
|
fs.writeFileSync(abs, fileContent, 'utf-8');
|
|
620
683
|
generatedFiles.push({ name: fileSpec.name, content: fileContent });
|
|
621
|
-
emit({ type: 'file_done', name: fileSpec.name, fi: fi + 1, total: filePlan.length, syntaxError });
|
|
684
|
+
emit({ type: 'file_done', name: fileSpec.name, fi: fi + 1, total: filePlan.length, syntaxError, tokOut: fileTokensOut });
|
|
622
685
|
} catch (e) {
|
|
623
686
|
emit({ type: 'file_error', name: fileSpec.name, error: e.message });
|
|
624
687
|
}
|
|
@@ -641,12 +704,10 @@ Output ONLY the complete file content.`;
|
|
|
641
704
|
fs.writeFileSync(memFile, `# ${projectName} — Project Memory\n\n_Add architectural decisions, preferences, and notes here._\n`, 'utf-8');
|
|
642
705
|
}
|
|
643
706
|
const logFile = path.join(ctxDir, 'changes.log.md');
|
|
644
|
-
const logEntry = `## ${new Date().toISOString().slice(0, 10)} — Initial generation\n- Generated ${generatedFiles.length} files\n- Description: ${description}\n`;
|
|
707
|
+
const logEntry = `## ${new Date().toISOString().slice(0, 10)} — Initial generation\n- Generated ${generatedFiles.length} files\n- Tokens in: ${totalTokensIn} / out: ${totalTokensOut}\n- Description: ${description}\n`;
|
|
645
708
|
fs.writeFileSync(logFile, (fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf-8') : '') + logEntry, 'utf-8');
|
|
646
709
|
|
|
647
|
-
|
|
648
|
-
const outputTokensEst = generatedFiles.reduce((sum, f) => sum + Math.ceil(f.content.length / 4), 0);
|
|
649
|
-
emit({ type: 'done', tokIn: inputTokensEst, tokOut: outputTokensEst });
|
|
710
|
+
emit({ type: 'done', tokIn: totalTokensIn, tokOut: totalTokensOut });
|
|
650
711
|
}
|
|
651
712
|
|
|
652
713
|
// ── ZIP download ──────────────────────────────────────────────────────────────
|