nothumanallowed 16.0.45 → 16.0.47
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 +76 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.47",
|
|
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.47';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -229,16 +229,42 @@ class SandboxManager {
|
|
|
229
229
|
}
|
|
230
230
|
} catch {}
|
|
231
231
|
|
|
232
|
-
// Pre-flight: auto-extend CSS
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
232
|
+
// Pre-flight: auto-extend CSS until 100% coverage (or LLM stops making
|
|
233
|
+
// progress). Target = 100%; max 5 passes as safety; early-exit if two
|
|
234
|
+
// consecutive passes don't reduce missing count (LLM stuck) or LLM fails.
|
|
235
|
+
// Goal: ZERO uncovered selectors when the LLM is capable of producing them.
|
|
236
236
|
try {
|
|
237
237
|
const projectName = path.basename(projectDir);
|
|
238
238
|
const cfg = loadConfig();
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
const maxPasses = 5;
|
|
240
|
+
let totalCovered = 0;
|
|
241
|
+
let prevMissing = Infinity;
|
|
242
|
+
let stuckPasses = 0;
|
|
243
|
+
for (let pass = 1; pass <= maxPasses; pass++) {
|
|
244
|
+
const styleResult = await _autoExtendStylesIfNeeded(projectName, cfg, emit, { minCoverage: 1.0 });
|
|
245
|
+
if (!styleResult.extended) {
|
|
246
|
+
if (styleResult.reason === 'coverage acceptable') break; // already 100%
|
|
247
|
+
// LLM failed or skipped — stop loop
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
const newlyCovered = (styleResult.missingBefore || 0) - (styleResult.missingAfter || 0);
|
|
251
|
+
totalCovered += newlyCovered;
|
|
252
|
+
const missingNow = styleResult.missingAfter || 0;
|
|
253
|
+
emit({ type: 'status', msg: `Pass ${pass}/${maxPasses}: ${styleResult.file} extended — ${newlyCovered} new selectors covered (${missingNow} still missing, ${((styleResult.coverageAfter || 0) * 100).toFixed(0)}% coverage).` });
|
|
254
|
+
if (missingNow === 0) {
|
|
255
|
+
emit({ type: 'status', msg: `100% CSS coverage reached after ${pass} pass${pass === 1 ? '' : 'es'}. Total ${totalCovered} selectors covered.` });
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
if (missingNow >= prevMissing) {
|
|
259
|
+
stuckPasses++;
|
|
260
|
+
if (stuckPasses >= 2) {
|
|
261
|
+
emit({ type: 'warn', msg: `LLM stopped making progress on extension (${missingNow} selectors still missing — likely pseudo-classes or JS-state classes the model can't infer). Stopping at ${((styleResult.coverageAfter || 0) * 100).toFixed(0)}%.` });
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
stuckPasses = 0;
|
|
266
|
+
}
|
|
267
|
+
prevMissing = missingNow;
|
|
242
268
|
}
|
|
243
269
|
} catch (e) {
|
|
244
270
|
emit({ type: 'warn', msg: `Auto-extend styles failed: ${(e.message || e).slice(0, 200)}` });
|
|
@@ -4254,27 +4280,57 @@ export async function _autoExtendStylesIfNeeded(projectName, config, emit, opts)
|
|
|
4254
4280
|
} catch {}
|
|
4255
4281
|
}
|
|
4256
4282
|
|
|
4257
|
-
const sys =
|
|
4283
|
+
const sys = `You are an expert frontend designer producing PRODUCTION-QUALITY CSS.
|
|
4284
|
+
|
|
4285
|
+
DESIGN REQUIREMENTS (NON-NEGOTIABLE):
|
|
4286
|
+
- WCAG AA contrast: text-on-background ratio >= 4.5:1. NO washed-out pastels for text. Body text must be near-black on light bg, or near-white on dark bg.
|
|
4287
|
+
- Use VIBRANT accent colors with sufficient saturation (HSL S >= 60%, L between 35-65% for accents).
|
|
4288
|
+
- Cover EVERY selector listed — including footer, header, nav, hero, sections, cards, buttons, forms, modals, tooltips.
|
|
4289
|
+
- Use modern CSS: flex/grid layouts, custom properties for colors, smooth transitions (200-300ms), subtle shadows.
|
|
4290
|
+
|
|
4291
|
+
OUTPUT: ONLY the complete extended CSS file content. No markdown fences, no explanations, no preamble. The output will be written directly to disk.`;
|
|
4258
4292
|
const user =
|
|
4259
4293
|
`Extend this CSS file: \`${targetRel}\`\n\n` +
|
|
4260
4294
|
`Current CSS (${target.size} bytes, ${analysis.cssRuleCount} rules):\n\`\`\`css\n${target.content.slice(0, 6000)}\n\`\`\`\n\n` +
|
|
4261
|
-
`
|
|
4295
|
+
`ALL missing selectors (${analysis.missing.length} — cover every single one):\n${analysis.missing.join(', ')}\n\n` +
|
|
4262
4296
|
`HTML files for context:\n${htmlSample}\n\n` +
|
|
4263
|
-
`Output the COMPLETE extended CSS file
|
|
4264
|
-
`- img { max-width: 100%; height: auto; object-fit: cover; }\n` +
|
|
4265
|
-
`-
|
|
4266
|
-
`-
|
|
4267
|
-
`-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4297
|
+
`Output the COMPLETE extended CSS file. Required additions:\n` +
|
|
4298
|
+
`- img { max-width: 100%; height: auto; object-fit: cover; display: block; }\n` +
|
|
4299
|
+
`- Footer, header, nav with proper layout and visible styling (NOT transparent backgrounds with low-contrast text)\n` +
|
|
4300
|
+
`- Rules for EVERY one of the ${analysis.missing.length} missing selectors above\n` +
|
|
4301
|
+
`- Responsive breakpoints at 1024px, 768px, 480px (mobile-first)\n` +
|
|
4302
|
+
`- Hover/focus/transition states for buttons, links, cards\n` +
|
|
4303
|
+
`- Color contrast must pass WCAG AA: text-on-bg >= 4.5:1`;
|
|
4304
|
+
|
|
4305
|
+
if (emit) emit({ type: 'status', msg: `CSS coverage ${(analysis.coverage * 100).toFixed(0)}% (${analysis.missing.length} selectors missing). Auto-extending ${targetRel} via LLM (timeout 60s)...` });
|
|
4306
|
+
|
|
4307
|
+
// Call LLM with timeout + progress heartbeat. Otherwise a slow/stuck Liara
|
|
4308
|
+
// leaves the user staring at "Auto-extending..." forever with no feedback.
|
|
4271
4309
|
let body = '';
|
|
4310
|
+
let lastChunkAt = Date.now();
|
|
4311
|
+
const startedAt = Date.now();
|
|
4312
|
+
const timeoutMs = 60_000;
|
|
4313
|
+
const heartbeatInterval = setInterval(() => {
|
|
4314
|
+
if (!emit) return;
|
|
4315
|
+
const elapsed = ((Date.now() - startedAt) / 1000).toFixed(0);
|
|
4316
|
+
const sinceLast = ((Date.now() - lastChunkAt) / 1000).toFixed(0);
|
|
4317
|
+
emit({ type: 'status', msg: `LLM extend: ${elapsed}s elapsed, ${body.length} bytes received (${sinceLast}s since last chunk)` });
|
|
4318
|
+
}, 5_000);
|
|
4319
|
+
|
|
4272
4320
|
try {
|
|
4273
|
-
await
|
|
4321
|
+
await Promise.race([
|
|
4322
|
+
callLLMStream(config, sys, user, (chunk) => {
|
|
4323
|
+
body += chunk;
|
|
4324
|
+
lastChunkAt = Date.now();
|
|
4325
|
+
}, { max_tokens: 8192 }),
|
|
4326
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('LLM timeout after ' + (timeoutMs / 1000) + 's')), timeoutMs)),
|
|
4327
|
+
]);
|
|
4274
4328
|
} catch (e) {
|
|
4275
|
-
|
|
4276
|
-
|
|
4329
|
+
clearInterval(heartbeatInterval);
|
|
4330
|
+
if (emit) emit({ type: 'warn', msg: `CSS extend failed: ${(e.message || e).slice(0, 200)}. Sandbox continues with current CSS — open chat to extend manually.` });
|
|
4331
|
+
return { extended: false, reason: 'llm_failed', error: e.message, partialBytes: body.length };
|
|
4277
4332
|
}
|
|
4333
|
+
clearInterval(heartbeatInterval);
|
|
4278
4334
|
|
|
4279
4335
|
// Strip markdown fences
|
|
4280
4336
|
body = body.replace(/^```[a-zA-Z]*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
|