nothumanallowed 14.5.3 → 14.5.4

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.5.3",
3
+ "version": "14.5.4",
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.5.3';
8
+ export const VERSION = '14.5.4';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -2100,6 +2100,73 @@ export function register(router) {
2100
2100
  } catch (e) { sendError(res, 500, e.message); }
2101
2101
  });
2102
2102
 
2103
+ // ── Full project scan — lint ALL files at once ───────────────────────────
2104
+ router.post('/api/studio/webcraft/scan', async (req, res) => {
2105
+ try {
2106
+ const { projectName } = await parseBody(req);
2107
+ if (!projectName) return sendError(res, 400, 'projectName required');
2108
+ const dir = ProjectStore.dir(projectName);
2109
+ if (!fs.existsSync(dir)) return sendJSON(res, 200, { issues: [] });
2110
+
2111
+ const files = _listProjectFiles(dir);
2112
+ const issues = [];
2113
+ const allFileNames = new Set(files);
2114
+
2115
+ for (const relPath of files) {
2116
+ const content = ProjectStore.readFile(projectName, relPath);
2117
+ if (!content) continue;
2118
+ const ext = relPath.split('.').pop()?.toLowerCase();
2119
+
2120
+ // JS syntax check
2121
+ if (ext === 'js' || ext === 'mjs') {
2122
+ try { new Function(content); } catch (e) {
2123
+ issues.push({ file: relPath, severity: 'error', message: `Syntax: ${e.message.replace(/\n.*/s, '')}` });
2124
+ }
2125
+ }
2126
+
2127
+ // JSON parse check
2128
+ if (ext === 'json') {
2129
+ try { JSON.parse(content); } catch (e) {
2130
+ issues.push({ file: relPath, severity: 'error', message: `JSON: ${e.message}` });
2131
+ }
2132
+ }
2133
+
2134
+ // CSS brace balance
2135
+ if (ext === 'css') {
2136
+ const o = (content.match(/\{/g) || []).length, c = (content.match(/\}/g) || []).length;
2137
+ if (o !== c) issues.push({ file: relPath, severity: 'warning', message: `Unbalanced braces: ${o} open, ${c} close` });
2138
+ }
2139
+
2140
+ // HTML closing tag + reference check
2141
+ if (ext === 'html' || ext === 'htm') {
2142
+ if (!content.includes('</html>')) {
2143
+ issues.push({ file: relPath, severity: 'warning', message: 'Missing </html>' });
2144
+ }
2145
+ // Check file references
2146
+ const refRegex = /(?:src|href)=["']([^"']*?\.(?:js|css|mjs))["']/gi;
2147
+ let m;
2148
+ while ((m = refRegex.exec(content)) !== null) {
2149
+ const ref = m[1];
2150
+ if (ref.startsWith('http') || ref.startsWith('//') || ref.startsWith('data:')) continue;
2151
+ const htmlDir = path.dirname(relPath);
2152
+ const refPath = ref.startsWith('/') ? ref.slice(1) : path.join(htmlDir, ref).replace(/\\/g, '/');
2153
+ const publicRef = refPath.startsWith('public/') ? refPath : `public/${refPath}`;
2154
+ if (!allFileNames.has(refPath) && !allFileNames.has(publicRef) && !allFileNames.has(ref)) {
2155
+ issues.push({ file: relPath, severity: 'error', message: `Missing file: ${ref}` });
2156
+ }
2157
+ }
2158
+ }
2159
+
2160
+ // Truncation check
2161
+ if (isFileTruncated(content, relPath)) {
2162
+ issues.push({ file: relPath, severity: 'warning', message: 'File appears truncated' });
2163
+ }
2164
+ }
2165
+
2166
+ sendJSON(res, 200, { issues, scanned: files.length });
2167
+ } catch (e) { sendError(res, 500, e.message); }
2168
+ });
2169
+
2103
2170
  // ── Syntax check ──────────────────────────────────────────────────────────
2104
2171
  router.post('/api/studio/webcraft/syntax-check', async (req, res) => {
2105
2172
  try {