nothumanallowed 15.1.32 → 15.1.34
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": "15.1.
|
|
3
|
+
"version": "15.1.34",
|
|
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/config.mjs
CHANGED
|
@@ -318,6 +318,14 @@ export function setConfigValue(key, value) {
|
|
|
318
318
|
'telegram-bot-token': 'responder.telegram.token',
|
|
319
319
|
'discord-bot-token': 'responder.discord.token',
|
|
320
320
|
'responder-auto-route': 'responder.autoRoute',
|
|
321
|
+
// Telegram bot persona — what name the bot uses when replying. If empty,
|
|
322
|
+
// falls back to the internal agent name (HERALD/ATHENA/...). Most users
|
|
323
|
+
// want a single consistent identity like "Agata" or "Jarvis".
|
|
324
|
+
'bot-name': 'responder.telegram.botName',
|
|
325
|
+
'botname': 'responder.telegram.botName',
|
|
326
|
+
'telegram-bot-name': 'responder.telegram.botName',
|
|
327
|
+
'persona-name': 'responder.telegram.botName',
|
|
328
|
+
'persona-mode': 'responder.telegram.personaMode', // persona | persona-only | persona+role | agent
|
|
321
329
|
'proactive': 'ops.proactive.enabled',
|
|
322
330
|
'proactive-email': 'ops.proactive.emailFollowUp',
|
|
323
331
|
'proactive-meeting': 'ops.proactive.meetingPrep',
|
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.34';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -822,17 +822,50 @@ class TelegramResponder {
|
|
|
822
822
|
const shortConfirm = /^(s[ìi]\.?|ok\.?|okay\.?|yes\.?|yep\.?|sure\.?|certo\.?|procedi\.?|fallo\.?|vai\.?|go\.?|do it\.?|conferma\.?|certo che s[ìi]\.?|fallo pure\.?)$/i.test(cleanText.trim());
|
|
823
823
|
|
|
824
824
|
if (proposedAction && shortConfirm) {
|
|
825
|
+
// Extract the SPECIFIC action the assistant proposed (create/delete/
|
|
826
|
+
// update/move/send/...), so we can inject an explicit tool name in
|
|
827
|
+
// the instruction. Without this, Liara may pick a random tool —
|
|
828
|
+
// e.g. user confirms a CREATE proposal and the model fires DELETE
|
|
829
|
+
// with a hallucinated eventId.
|
|
830
|
+
const r = lowerReply;
|
|
831
|
+
let actionHint = '';
|
|
832
|
+
let toolHint = '';
|
|
833
|
+
if (/fissat[oa]|inserir[oe]|cre(a|er)[oa]|aggiunger[oeò]|prenota|registr[oa]|mett[oa] (?:in calendario|nel calendario)/.test(r)) {
|
|
834
|
+
actionHint = 'CREARE un nuovo appuntamento';
|
|
835
|
+
toolHint = 'calendar_create con i parametri ESATTI (summary, start, end) che hai descritto nel turno precedente';
|
|
836
|
+
} else if (/cancell|eliminar[eo]|rimuover/.test(r)) {
|
|
837
|
+
actionHint = 'CANCELLARE un appuntamento esistente';
|
|
838
|
+
toolHint = 'PRIMA calendar_find/calendar_date per ottenere l\'eventId REALE, POI calendar_delete con quell\'eventId. Mai inventare ID';
|
|
839
|
+
} else if (/spostar|riprogrammar|cambiar[eo] (?:l\')?orario|cambiar[eo] (?:la )?data/.test(r)) {
|
|
840
|
+
actionHint = 'SPOSTARE un appuntamento';
|
|
841
|
+
toolHint = 'PRIMA calendar_find per ottenere eventId, POI calendar_move(eventId, newStart, newEnd)';
|
|
842
|
+
} else if (/modificar|correggere|rinominar|cambiar[eo] (?:il )?titolo/.test(r)) {
|
|
843
|
+
actionHint = 'MODIFICARE un appuntamento';
|
|
844
|
+
toolHint = 'PRIMA calendar_find per eventId, POI calendar_update(eventId, ...) con SOLO i campi da cambiare';
|
|
845
|
+
} else if (/inviar|spedir|mandar (?:l\'|la )?email|send (?:the )?email/.test(r)) {
|
|
846
|
+
actionHint = 'INVIARE un\'email';
|
|
847
|
+
toolHint = 'gmail_send (o gmail_reply se è una risposta) con destinatario, oggetto e corpo già concordati';
|
|
848
|
+
} else if (/aggiunger[eo] (?:un )?task|creare (?:un )?task|inserir[eo] (?:il )?task/.test(r)) {
|
|
849
|
+
actionHint = 'CREARE un task';
|
|
850
|
+
toolHint = 'task_add con la descrizione concordata';
|
|
851
|
+
} else {
|
|
852
|
+
actionHint = 'l\'azione che hai proposto';
|
|
853
|
+
toolHint = 'il tool corrispondente all\'azione (calendar_create se hai detto "fisso"; calendar_delete se "cancello"; gmail_send se "invio"; ecc.). NON inventare eventId — usa SOLO ID reali ricevuti da tool precedenti';
|
|
854
|
+
}
|
|
855
|
+
|
|
825
856
|
enrichedMessage =
|
|
826
|
-
`[CONFERMA UTENTE — Esegui SUBITO l'azione che hai proposto
|
|
827
|
-
`Tuo turno precedente:\n"${(lastCtx.agentReply || '').slice(0, 800)}"\n\n` +
|
|
857
|
+
`[CONFERMA UTENTE — Esegui SUBITO l'azione che hai proposto]\n\n` +
|
|
858
|
+
`Tuo turno precedente (proposta):\n"${(lastCtx.agentReply || '').slice(0, 800)}"\n\n` +
|
|
828
859
|
`Risposta utente: "${cleanText.trim()}"\n\n` +
|
|
860
|
+
`AZIONE DA ESEGUIRE: ${actionHint}\n` +
|
|
861
|
+
`TOOL DA USARE: ${toolHint}\n\n` +
|
|
829
862
|
`ISTRUZIONI OBBLIGATORIE:\n` +
|
|
830
|
-
`1.
|
|
831
|
-
`2.
|
|
832
|
-
`3. Se
|
|
833
|
-
`4.
|
|
834
|
-
`5. NON
|
|
835
|
-
this.log(`[Telegram] ${fromUser}: pending-action force
|
|
863
|
+
`1. Emetti SUBITO il blocco JSON \`\`\`json {"action":"...","params":{...}} \`\`\` per ESATTAMENTE l'azione qui sopra.\n` +
|
|
864
|
+
`2. Usa i parametri (data, ora, titolo, destinatario, eccetera) che HAI GIÀ menzionato nel tuo turno precedente. NON chiedere di nuovo.\n` +
|
|
865
|
+
`3. Se l'azione è CREATE/SEND/ADD: emetti SOLO quel tool. NIENTE find/delete/update preliminare — quei tool servono per modificare cose esistenti, non per creare di nuove.\n` +
|
|
866
|
+
`4. Se l'azione è DELETE/UPDATE/MOVE: emetti PRIMA calendar_find o calendar_date per trovare l'eventId REALE (lungo, alfanumerico Google). MAI usare placeholder come "A1B2C3D4E5F6G7H8I9J0", "abc123", "ABC123".\n` +
|
|
867
|
+
`5. NON chiedere "cosa vuoi". NON dire "Procedo." senza eseguire. SOLO il blocco JSON e una breve conferma in italiano dopo l'esecuzione.`;
|
|
868
|
+
this.log(`[Telegram] ${fromUser}: pending-action force → ${actionHint}`);
|
|
836
869
|
}
|
|
837
870
|
|
|
838
871
|
this.log(`[Telegram] ${fromUser}: continuation → ${agent.toUpperCase()} (ctx ${Math.round(stickyAge/1000)}s ago, history=${preHistory ? preHistory.length : 0})`);
|
|
@@ -887,12 +920,34 @@ class TelegramResponder {
|
|
|
887
920
|
// routes the confirmation to a random agent (FORGE in the reported bug).
|
|
888
921
|
this._persistContext();
|
|
889
922
|
|
|
923
|
+
// Bot persona name — user can configure a custom name (e.g. "Agata")
|
|
924
|
+
// so the bot speaks with a single consistent identity instead of
|
|
925
|
+
// leaking the internal multi-agent routing (HERALD/ATHENA/MERCURY).
|
|
926
|
+
// Mode determines the format:
|
|
927
|
+
// - 'persona-only' → just the body, no prefix (most natural)
|
|
928
|
+
// - 'persona' → "[BotName] body"
|
|
929
|
+
// - 'persona+role' → "[BotName · role] body" — keeps specialization hint
|
|
930
|
+
// - 'agent' → "[AGENT_NAME] body" — legacy / debugging mode
|
|
931
|
+
const personaName = this.config.responder?.telegram?.botName || this.config.responder?.botName || '';
|
|
932
|
+
const personaMode = this.config.responder?.telegram?.personaMode || (personaName ? 'persona' : 'agent');
|
|
933
|
+
const agentLabel = String(agent || '').toUpperCase();
|
|
934
|
+
let prefixedText;
|
|
935
|
+
if (personaMode === 'persona-only' && personaName) {
|
|
936
|
+
prefixedText = truncated;
|
|
937
|
+
} else if (personaMode === 'persona+role' && personaName) {
|
|
938
|
+
prefixedText = `[${personaName} · ${agentLabel.toLowerCase()}]\n\n${truncated}`;
|
|
939
|
+
} else if (personaMode === 'persona' && personaName) {
|
|
940
|
+
prefixedText = `[${personaName}]\n\n${truncated}`;
|
|
941
|
+
} else {
|
|
942
|
+
prefixedText = `[${agentLabel}]\n\n${truncated}`;
|
|
943
|
+
}
|
|
944
|
+
|
|
890
945
|
await this._telegramCall('sendMessage', {
|
|
891
946
|
chat_id: chatId,
|
|
892
|
-
text:
|
|
947
|
+
text: prefixedText,
|
|
893
948
|
});
|
|
894
949
|
|
|
895
|
-
this.log(`[Telegram] Responded to ${fromUser} via ${
|
|
950
|
+
this.log(`[Telegram] Responded to ${fromUser} via ${agentLabel}${personaName ? ` (as "${personaName}")` : ''} (${responseText.length} chars)${isCompletedAction(responseText) ? ' [action completed — context reset]' : ''}`);
|
|
896
951
|
} catch (err) {
|
|
897
952
|
this.log(`[Telegram] Agent call failed: ${err.message}`);
|
|
898
953
|
// Send error message to user
|
|
@@ -1091,6 +1091,41 @@ export async function buildSystemPrompt(persona, personaDescription, config, ini
|
|
|
1091
1091
|
* @param {object} config — NHA config
|
|
1092
1092
|
* @returns {Promise<string>} result description
|
|
1093
1093
|
*/
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Detect IDs that the model has clearly invented vs real Google Calendar
|
|
1097
|
+
* eventIds. Real ones are LONG (≥10), LOWERCASE, alphanumeric (a-v + 0-9,
|
|
1098
|
+
* sometimes with `_` or `-`). Placeholders are typically short, uppercase,
|
|
1099
|
+
* dictionary words, or alternate-letter+digit pseudo-random patterns like
|
|
1100
|
+
* "A1B2C3D4E5F6G7H8I9J0".
|
|
1101
|
+
*/
|
|
1102
|
+
function isPlaceholderEventId(id) {
|
|
1103
|
+
if (!id || typeof id !== 'string') return true;
|
|
1104
|
+
const s = id.trim();
|
|
1105
|
+
// 1. Explicit literal blocklist (covers everything we've seen so far)
|
|
1106
|
+
const LITERAL = new Set([
|
|
1107
|
+
'abc123', 'def456', 'xyz789', 'event_123', 'event_123456789',
|
|
1108
|
+
'123', '456', 'foo', 'bar', 'baz', 'example', 'placeholder',
|
|
1109
|
+
'eventid', 'event_id', 'id', 'null', 'undefined', 'some-google-id',
|
|
1110
|
+
'event-id', 'id-from-tool', 'event_abc', 'event_xyz',
|
|
1111
|
+
]);
|
|
1112
|
+
if (LITERAL.has(s.toLowerCase())) return true;
|
|
1113
|
+
// 2. Too short — Google eventIds are always ≥ 16 chars in practice
|
|
1114
|
+
if (s.length < 10) return true;
|
|
1115
|
+
// 3. Contains spaces — never valid
|
|
1116
|
+
if (/\s/.test(s)) return true;
|
|
1117
|
+
// 4. Pseudo-random alternating letter+digit pattern like "A1B2C3D4..."
|
|
1118
|
+
// Real Google eventIds NEVER look like this — they have natural
|
|
1119
|
+
// base32-ish randomness, not human-readable patterns.
|
|
1120
|
+
if (/^[A-Z](?:\d[A-Z]){3,}/i.test(s)) return true;
|
|
1121
|
+
// 5. Almost-all-uppercase: Google eventIds are lowercase. UPPERCASE
|
|
1122
|
+
// is a model invention signature.
|
|
1123
|
+
const upper = (s.match(/[A-Z]/g) || []).length;
|
|
1124
|
+
const lower = (s.match(/[a-z]/g) || []).length;
|
|
1125
|
+
if (upper >= 3 && upper > lower) return true;
|
|
1126
|
+
return false;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1094
1129
|
export async function executeTool(action, params, config) {
|
|
1095
1130
|
switch (action) {
|
|
1096
1131
|
// ── Gmail ──────────────────────────────────────────────────────────────
|
|
@@ -1634,14 +1669,10 @@ export async function executeTool(action, params, config) {
|
|
|
1634
1669
|
let eventId = String(params.eventId || '').trim();
|
|
1635
1670
|
if (!eventId) return 'eventId required. Call calendar_find or calendar_date first to get the REAL eventId.';
|
|
1636
1671
|
|
|
1637
|
-
// Reject obvious placeholder IDs (model copied from prompt examples
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
'eventid', 'event_id', 'id', 'null', 'undefined',
|
|
1642
|
-
]);
|
|
1643
|
-
if (PLACEHOLDERS.has(eventId.toLowerCase())) {
|
|
1644
|
-
return `"${eventId}" is a placeholder, not a real eventId. Call calendar_find or calendar_date FIRST to get real eventIds.`;
|
|
1672
|
+
// Reject obvious placeholder IDs (model copied from prompt examples or
|
|
1673
|
+
// emitted obvious patterns like "A1B2C3D4E5F6G7H8I9J0").
|
|
1674
|
+
if (isPlaceholderEventId(eventId)) {
|
|
1675
|
+
return `"${eventId}" looks invented (placeholder pattern). Call calendar_find or calendar_date FIRST to get real eventIds.`;
|
|
1645
1676
|
}
|
|
1646
1677
|
|
|
1647
1678
|
// Smart eventId resolution: if it looks like a name instead of a Google Calendar ID, search for it
|
|
@@ -1695,16 +1726,11 @@ export async function executeTool(action, params, config) {
|
|
|
1695
1726
|
let delEventId = String(params.eventId).trim();
|
|
1696
1727
|
|
|
1697
1728
|
// Reject placeholder IDs the model may have copied verbatim from prompt
|
|
1698
|
-
// examples or hallucinated wholesale.
|
|
1699
|
-
//
|
|
1700
|
-
//
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
'123', '456', 'foo', 'bar', 'baz', 'example', 'placeholder',
|
|
1704
|
-
'eventid', 'event_id', 'id', 'null', 'undefined',
|
|
1705
|
-
]);
|
|
1706
|
-
if (PLACEHOLDER_IDS.has(delEventId.toLowerCase())) {
|
|
1707
|
-
return `"${delEventId}" is a placeholder, not a real eventId. Call calendar_date(YYYY-MM-DD) or calendar_find(query) FIRST to get the real eventIds, then call calendar_delete with one of those values.`;
|
|
1729
|
+
// examples or hallucinated wholesale. Real Google Calendar eventIds
|
|
1730
|
+
// are long LOWERCASE alphanumeric strings — never UPPERCASE patterns
|
|
1731
|
+
// like "A1B2C3D4E5F6G7H8I9J0" (visibly invented).
|
|
1732
|
+
if (isPlaceholderEventId(delEventId)) {
|
|
1733
|
+
return `"${delEventId}" looks invented (placeholder pattern). Real Google Calendar eventIds are long lowercase alphanumeric strings (a-v + 0-9, often with - and _). Call calendar_date(YYYY-MM-DD) or calendar_find(query) FIRST to get the real eventIds, then call calendar_delete with one of those.`;
|
|
1708
1734
|
}
|
|
1709
1735
|
|
|
1710
1736
|
// Branch A — looks like a free-text search query (spaces, caps, very short).
|
|
@@ -788,7 +788,7 @@ FRONTEND:
|
|
|
788
788
|
- index.html: Job board homepage — (1) Hero: gradient background, large search widget (keyword input + location input + category select + Search button), quick stats bar (12,847 Jobs / 3,420 Companies / 45K Candidates). (2) Featured jobs: horizontal scroll row of 4 "Featured" cards with company logo (colored emoji), job title, company name, location + type badge + salary range + "Easy Apply" button. (3) Main layout: left sidebar filters (Job Type checkboxes, Salary Range slider, Category checkboxes, Location input, Posted Within select, Remote Only toggle) + right: jobs list (job row cards with company logo/title/company/location/type/salary/time-ago posted/Save bookmark/Apply button). (4) "Load More" button + results count "Showing 12 of 247 jobs". (5) Top companies section: 8 company cards with logo/name/industry/openings count. (6) Category browsing: 6 category cards with icon/name/count/link. (7) Job seeker CTA banner. (8) Newsletter.
|
|
789
789
|
- job.html: Job detail page — company header (logo + name + industry + website + size + "Follow" button), job title + badges (Remote, Senior, Urgent), salary + location + posted date, Apply Now button (sticky on scroll), job description (markdown-rendered sections: About/Responsibilities/Requirements/Nice-to-Have/Benefits), company sidebar card (description + stats), similar jobs list (4 cards)
|
|
790
790
|
- apply.html: Application modal/page — job summary header, form (Full Name / Email / Phone / LinkedIn URL / Portfolio URL / Cover Letter textarea with character count / Resume paste textarea), Submit button with loading state, success page with application reference number
|
|
791
|
-
- public/css/main.css: Modern job board design — tag/badge system (Remote=blue, Onsite=green, Urgent=red), salary range display, company logo placeholder styles, filter sidebar collapse on mobile, job card hover effects, application form validation styles`}],JT=[{key:`auth`,label:`Auth (register/login/JWT)`,icon:`🔒`},{key:`cookieBanner`,label:`GDPR Cookie Banner`,icon:`🍪`},{key:`securityMiddleware`,label:`Security Middleware`,icon:`🛡️`},{key:`emailVerification`,label:`Email Verification`,icon:`✉️`}],YT={js:`📄`,ts:`📄`,css:`🎨`,html:`🌐`,json:`📋`,md:`📑`,sql:`🗂`,env:`🔐`,conf:`⚙`,lock:`🔒`};function XT(e){return YT[e.split(`.`).pop()?.toLowerCase()??``]??`📄`}function ZT(e){let t=new TextEncoder().encode(e).length;return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}function QT(e){return e===`memory`?`🧠`:e===`provider`?`🤖`:e===`log`?`📄`:`📋`}function $T(){let e=j(),[t,n]=(0,_.useState)(`new`),[r,i]=(0,_.useState)(`files`),[a,o]=(0,_.useState)(``),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)({auth:!0,cookieBanner:!0,securityMiddleware:!0,emailVerification:!0}),[d,f]=(0,_.useState)([{label:`Email`,type:`email`,required:!0},{label:`Password`,type:`password`,required:!0},{label:`Name`,type:`text`,required:!0}]),[p,m]=(0,_.useState)([]),[h,g]=(0,_.useState)(0),[v,y]=(0,_.useState)(null),[b,x]=(0,_.useState)(null),[S,C]=(0,_.useState)(new Set),[w,ee]=(0,_.useState)(!1),[te,T]=(0,_.useState)(``),[ne,re]=(0,_.useState)(``),[ie,O]=(0,_.useState)(!1),[ae,A]=(0,_.useState)(null),[oe,se]=(0,_.useState)(!1),[ce,le]=(0,_.useState)([]),[ue,de]=(0,_.useState)([]),M=(0,_.useRef)(null),fe=(0,_.useRef)(null),pe=(0,_.useRef)(!0),me=(0,_.useRef)(null),he=(0,_.useRef)(null),ge=(0,_.useRef)(null),_e=(0,_.useRef)(null),[ve,ye]=(0,_.useState)(null),[be,xe]=(0,_.useState)(!1),[Se,N]=(0,_.useState)(!1),[Ce,we]=(0,_.useState)(0),[Te,Ee]=(0,_.useState)(0),[P,De]=(0,_.useState)(``),[Oe,ke]=(0,_.useState)({fi:0,total:0,name:``}),[F,I]=(0,_.useState)({tokIn:0,tokOut:0}),[Ae,je]=(0,_.useState)(null),[Me,Ne]=(0,_.useState)([]),[Pe,Fe]=(0,_.useState)(``),[Ie,Le]=(0,_.useState)(!1),[Re,ze]=(0,_.useState)([]),[L,Be]=(0,_.useState)(null),[Ve,He]=(0,_.useState)([]),[Ue,We]=(0,_.useState)(!1),[Ge,Ke]=(0,_.useState)(null),[qe,Je]=(0,_.useState)([]),[Ye,Xe]=(0,_.useState)(!1),[Ze,Qe]=(0,_.useState)(``),[$e,et]=(0,_.useState)([]),[tt,nt]=(0,_.useState)([]),[rt,it]=(0,_.useState)([]),[at,ot]=(0,_.useState)(null),[st,ct]=(0,_.useState)(!1),[lt,ut]=(0,_.useState)(!1),[dt,ft]=(0,_.useState)(null),[pt,mt]=(0,_.useState)([]),[ht,gt]=(0,_.useState)(`0s`),[_t,vt]=(0,_.useState)(`0s`),yt=(0,_.useRef)(0),R=(0,_.useRef)(0),bt=(0,_.useRef)(null),xt=(0,_.useRef)(null),St=(0,_.useRef)(null),Ct=(0,_.useRef)(null),wt=(0,_.useRef)(null),Tt=(0,_.useRef)(!1),Et=(0,_.useRef)(null);function z(e){console.log(`[WC-SCAN] scanning:`,e),D(`/api/studio/webcraft/scan`,{projectName:e}).then(e=>{console.log(`[WC-SCAN] result:`,e?.issues?.length,`issues`),e?.issues!==void 0&&de(e.issues)}).catch(e=>{console.error(`[WC-SCAN] error:`,e)})}(0,_.useEffect)(()=>{if(!at){le([]);return}let e=setInterval(()=>{E(`/api/studio/webcraft/sandbox/errors`).then(e=>{e?.errors?.length&&le(e.errors)}).catch(()=>{})},5e3);return()=>clearInterval(e)},[at]);let Dt=(0,_.useRef)(at);Dt.current=at,(0,_.useEffect)(()=>{let e=()=>{Dt.current&&(navigator.sendBeacon(`/api/studio/webcraft/sandbox/stop-beacon`,``),fetch(`/api/studio/webcraft/sandbox`,{method:`DELETE`,keepalive:!0}).catch(()=>{}))};return window.addEventListener(`beforeunload`,e),()=>{window.removeEventListener(`beforeunload`,e),e()}},[]),(0,_.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&e.key===`f`&&(e.preventDefault(),ee(e=>!e)),(e.metaKey||e.ctrlKey)&&e.key===`s`&&(e.preventDefault(),b!==null&&p[h])){let e=p[h];m(e=>e.map((e,t)=>t===h?{...e,content:b}:e)),D(`/api/studio/webcraft/file/write`,{projectName:a,path:e.name,content:b}),x(null),C(t=>{let n=new Set(t);return n.delete(e.name),n})}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[b,h,p,a]),(0,_.useEffect)(()=>{let e=()=>{let e=b===null?ge.current:he.current;e&&_e.current&&(_e.current.scrollTop=e.scrollTop)},t=b===null?ge.current:he.current;return t&&t.addEventListener(`scroll`,e),()=>{t&&t.removeEventListener(`scroll`,e)}},[b,h]);function Ot(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function kt(e){let t=Math.floor((Date.now()-e)/1e3),n=Math.floor(t/60);return(n>0?`${n}m `:``)+`${t%60}s`}(0,_.useEffect)(()=>(be||Se?bt.current=setInterval(()=>{be&>(kt(yt.current)),Se&&vt(kt(R.current))},1e3):bt.current&&=(clearInterval(bt.current),null),()=>{bt.current&&clearInterval(bt.current)}),[be,Se]);function At(){St.current&&(St.current.scrollTop=St.current.scrollHeight)}(0,_.useEffect)(()=>{At()},[Me]),(0,_.useEffect)(()=>{a
|
|
791
|
+
- public/css/main.css: Modern job board design — tag/badge system (Remote=blue, Onsite=green, Urgent=red), salary range display, company logo placeholder styles, filter sidebar collapse on mobile, job card hover effects, application form validation styles`}],JT=[{key:`auth`,label:`Auth (register/login/JWT)`,icon:`🔒`},{key:`cookieBanner`,label:`GDPR Cookie Banner`,icon:`🍪`},{key:`securityMiddleware`,label:`Security Middleware`,icon:`🛡️`},{key:`emailVerification`,label:`Email Verification`,icon:`✉️`}],YT={js:`📄`,ts:`📄`,css:`🎨`,html:`🌐`,json:`📋`,md:`📑`,sql:`🗂`,env:`🔐`,conf:`⚙`,lock:`🔒`};function XT(e){return YT[e.split(`.`).pop()?.toLowerCase()??``]??`📄`}function ZT(e){let t=new TextEncoder().encode(e).length;return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}function QT(e){return e===`memory`?`🧠`:e===`provider`?`🤖`:e===`log`?`📄`:`📋`}function $T(){let e=j(),[t,n]=(0,_.useState)(`new`),[r,i]=(0,_.useState)(`files`),[a,o]=(0,_.useState)(``),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)({auth:!0,cookieBanner:!0,securityMiddleware:!0,emailVerification:!0}),[d,f]=(0,_.useState)([{label:`Email`,type:`email`,required:!0},{label:`Password`,type:`password`,required:!0},{label:`Name`,type:`text`,required:!0}]),[p,m]=(0,_.useState)([]),[h,g]=(0,_.useState)(0),[v,y]=(0,_.useState)(null),[b,x]=(0,_.useState)(null),[S,C]=(0,_.useState)(new Set),[w,ee]=(0,_.useState)(!1),[te,T]=(0,_.useState)(``),[ne,re]=(0,_.useState)(``),[ie,O]=(0,_.useState)(!1),[ae,A]=(0,_.useState)(null),[oe,se]=(0,_.useState)(!1),[ce,le]=(0,_.useState)([]),[ue,de]=(0,_.useState)([]),M=(0,_.useRef)(null),fe=(0,_.useRef)(null),pe=(0,_.useRef)(!0),me=(0,_.useRef)(null),he=(0,_.useRef)(null),ge=(0,_.useRef)(null),_e=(0,_.useRef)(null),[ve,ye]=(0,_.useState)(null),[be,xe]=(0,_.useState)(!1),[Se,N]=(0,_.useState)(!1),[Ce,we]=(0,_.useState)(0),[Te,Ee]=(0,_.useState)(0),[P,De]=(0,_.useState)(``),[Oe,ke]=(0,_.useState)({fi:0,total:0,name:``}),[F,I]=(0,_.useState)({tokIn:0,tokOut:0}),[Ae,je]=(0,_.useState)(null),[Me,Ne]=(0,_.useState)([]),[Pe,Fe]=(0,_.useState)(``),[Ie,Le]=(0,_.useState)(!1),[Re,ze]=(0,_.useState)([]),[L,Be]=(0,_.useState)(null),[Ve,He]=(0,_.useState)([]),[Ue,We]=(0,_.useState)(!1),[Ge,Ke]=(0,_.useState)(null),[qe,Je]=(0,_.useState)([]),[Ye,Xe]=(0,_.useState)(!1),[Ze,Qe]=(0,_.useState)(``),[$e,et]=(0,_.useState)([]),[tt,nt]=(0,_.useState)([]),[rt,it]=(0,_.useState)([]),[at,ot]=(0,_.useState)(null),[st,ct]=(0,_.useState)(!1),[lt,ut]=(0,_.useState)(!1),[dt,ft]=(0,_.useState)(null),[pt,mt]=(0,_.useState)([]),[ht,gt]=(0,_.useState)(`0s`),[_t,vt]=(0,_.useState)(`0s`),yt=(0,_.useRef)(0),R=(0,_.useRef)(0),bt=(0,_.useRef)(null),xt=(0,_.useRef)(null),St=(0,_.useRef)(null),Ct=(0,_.useRef)(null),wt=(0,_.useRef)(null),Tt=(0,_.useRef)(!1),Et=(0,_.useRef)(null);function z(e){console.log(`[WC-SCAN] scanning:`,e),D(`/api/studio/webcraft/scan`,{projectName:e}).then(e=>{console.log(`[WC-SCAN] result:`,e?.issues?.length,`issues`),e?.issues!==void 0&&de(e.issues)}).catch(e=>{console.error(`[WC-SCAN] error:`,e)})}(0,_.useEffect)(()=>{if(!at){le([]);return}let e=setInterval(()=>{E(`/api/studio/webcraft/sandbox/errors`).then(e=>{e?.errors?.length&&le(e.errors)}).catch(()=>{})},5e3);return()=>clearInterval(e)},[at]);let Dt=(0,_.useRef)(at);Dt.current=at,(0,_.useEffect)(()=>{let e=()=>{Dt.current&&(navigator.sendBeacon(`/api/studio/webcraft/sandbox/stop-beacon`,``),fetch(`/api/studio/webcraft/sandbox`,{method:`DELETE`,keepalive:!0}).catch(()=>{}))};return window.addEventListener(`beforeunload`,e),()=>{window.removeEventListener(`beforeunload`,e),e()}},[]),(0,_.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&e.key===`f`&&(e.preventDefault(),ee(e=>!e)),(e.metaKey||e.ctrlKey)&&e.key===`s`&&(e.preventDefault(),b!==null&&p[h])){let e=p[h];m(e=>e.map((e,t)=>t===h?{...e,content:b}:e)),D(`/api/studio/webcraft/file/write`,{projectName:a,path:e.name,content:b}),x(null),C(t=>{let n=new Set(t);return n.delete(e.name),n})}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[b,h,p,a]),(0,_.useEffect)(()=>{let e=()=>{let e=b===null?ge.current:he.current;e&&_e.current&&(_e.current.scrollTop=e.scrollTop)},t=b===null?ge.current:he.current;return t&&t.addEventListener(`scroll`,e),()=>{t&&t.removeEventListener(`scroll`,e)}},[b,h]);function Ot(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function kt(e){let t=Math.floor((Date.now()-e)/1e3),n=Math.floor(t/60);return(n>0?`${n}m `:``)+`${t%60}s`}(0,_.useEffect)(()=>(be||Se?bt.current=setInterval(()=>{be&>(kt(yt.current)),Se&&vt(kt(R.current))},1e3):bt.current&&=(clearInterval(bt.current),null),()=>{bt.current&&clearInterval(bt.current)}),[be,Se]);function At(){St.current&&(St.current.scrollTop=St.current.scrollHeight)}(0,_.useEffect)(()=>{At()},[Me]),(0,_.useEffect)(()=>{if(!a){He([]),We(!1);return}He([]),We(!1),E(`/api/studio/webcraft/skills/${encodeURIComponent(a)}`).then(e=>{e?.skills&&He(e.skills),We(!0)}).catch(()=>{We(!0)})},[a]);async function jt(e,t){if(be||!e||e.length<5)return;xe(!0),m([]),g(0),y(null),ke({fi:0,total:0,name:``}),I({tokIn:0,tokOut:0}),yt.current=Date.now(),gt(`0s`),xt.current=new AbortController;let n=Date.now();try{let r=await fetch(`/api/studio/webcraft/generate`,{method:`POST`,headers:{"Content-Type":`application/json`},signal:xt.current.signal,body:JSON.stringify({projectName:t,description:e,blocks:l,authFields:d})});if(!r.ok||!r.body){xe(!1);return}let i=r.body.getReader(),s=xt.current,c=new TextDecoder,u=``,f=[];for(;;){if(s?.signal?.aborted){try{i.cancel()}catch{}break}let{done:e,value:t}=await i.read();if(e)break;u+=c.decode(t,{stream:!0});let r=u.split(`
|
|
792
792
|
|
|
793
793
|
`);u=r.pop()??``;for(let e of r){let t=e.replace(/^data: /,``).trim();if(t)try{let e=JSON.parse(t);if(e.type===`project_renamed`)o(e.name);else if(e.type===`processing`||e.type===`planning`)ke(t=>({...t,name:e.type===`planning`?`📋 Pianificazione struttura...`:e.msg||`Avvio...`}));else if(e.type===`file_start`)f.push({name:e.name,content:``,_pending:!0}),m([...f]),ke({fi:e.fi,total:e.total,name:e.name}),M.current=f.length-1,fe.current||(g(f.length-1),y(``)),pe.current=!0;else if(e.type===`file_chunk`){let t=f.find(t=>t.name===e.name);t&&(t.content+=e.chunk,t._pending=!1),m([...f]);let n=f.findIndex(t=>t.name===e.name);n>=0&&(M.current=n,y(t?t.content:null))}else if(e.type===`file_done`){let t=f.find(t=>t.name===e.name);t&&(t._pending=!1,e.syntaxError&&(t._syntaxError=e.syntaxError)),m([...f]),ke({fi:e.fi,total:e.total,name:e.name}),(e.cumTokIn||e.cumTokOut)&&I({tokIn:e.cumTokIn||0,tokOut:e.cumTokOut||0})}else if(e.type===`file_error`){let t=f.find(t=>t.name===e.name);t&&(t._error=!0,t._pending=!1),m([...f])}else e.type===`phase`||e.type===`status`&&!e.op?ke(t=>({...t,name:e.msg||e.phase||``})):e.type===`done`&&(je({seconds:Math.round((Date.now()-n)/1e3),tokIn:e.tokIn??0,tokOut:e.tokOut??0,files:f.length}),y(null),xe(!1),M.current=null,fe.current&&=(clearTimeout(fe.current),null),a&&z(a),se(!0),We(!1),He([]),f.some(e=>e._error||e._syntaxError)?setTimeout(()=>wt.current?.(),800):setTimeout(()=>Et.current?.(),800))}catch{}}}}catch(e){e.name!==`AbortError`&&Ne(t=>[...t,{role:`system`,text:`Errore generazione: `+e.message}]),xe(!1)}}async function B(){let e=Pe.trim();if(!(a&&p.length>0)){if(!e||e.length<5)return;let t=a||`MyProject`;o(t),c(e),Fe(``),await jt(e,t);return}if(!e&&Re.length===0||Ie||be)return;let t=[...Re];if(ze([]),Fe(``),e.toLowerCase().startsWith(`/plan `)||e.toLowerCase().startsWith(`piano: `)){let t=e.replace(/^\/plan[ ]*/i,``).replace(/^piano:[ ]*/i,``);Ne(t=>[...t,{role:`user`,text:e}]),await Mt(`[MODALITA PIANO] Descrivi cosa modificheresti per: "${t}". Elenca i file e cosa faresti. NON applicare modifiche ancora. Rispondi con il piano in bullet list.`,t,[]);return}Ne(n=>[...n,{role:`user`,text:e,attachments:t}]),await Mt(e,null,t)}async function Mt(e,t,n){if(Ie)return;Le(!0),a&&p.length>0&&D(`/api/studio/webcraft/snapshot`,{projectName:a}).catch(()=>{});let r={};p.forEach(e=>{r[e.name]=e.content});try{let i=new AbortController;xt.current=i;let o=await fetch(`/api/studio/webcraft/agent`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({projectName:a,message:e,attachments:n.map(e=>({name:e.name,mimeType:e.mimeType,base64:e.base64}))}),signal:i.signal});if(!o.ok||!o.body){Ne(e=>[...e,{role:`agent`,text:`Errore: ${o.status}`,tools:[]}]),Le(!1);return}let s={role:`agent`,text:``,tools:[]};Ne(e=>[...e,s]);let c=o.body.getReader(),l=new TextDecoder,u=``,d=!1,f=``;for(;;){if(i.signal.aborted){try{c.cancel()}catch{}break}let{done:e,value:n}=await c.read();if(e)break;u+=l.decode(n,{stream:!0});let o=u.split(`
|
|
794
794
|
|
package/src/ui-dist/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
10
|
<title>NHA — NotHumanAllowed</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CJMAWGRD.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-DnJMrYkq.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|