nothumanallowed 13.5.105 → 13.5.106
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/commands/ui.mjs +154 -84
- package/src/constants.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.106",
|
|
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/commands/ui.mjs
CHANGED
|
@@ -3180,120 +3180,190 @@ ${rawText.slice(0, 18000)}`;
|
|
|
3180
3180
|
|
|
3181
3181
|
// ── Extract parameters from task ──
|
|
3182
3182
|
// Date: "16 maggio", "16/05", "16-05-2026", "sabato 16", etc.
|
|
3183
|
-
|
|
3184
|
-
|| travelTask.match(/(\d{1,2})[\/\-](\d{1,2})(?:[\/\-](\d{4}))?/)
|
|
3185
|
-
|| travelTask.match(/(monday|tuesday|wednesday|thursday|friday|saturday|sunday|luned|marted|mercoled|gioved|venerd|sabato|domenica)\s+\w+/i);
|
|
3183
|
+
// ── PARAMETER EXTRACTION ──
|
|
3186
3184
|
const monthNames = {gennaio:1,febbraio:2,marzo:3,aprile:4,maggio:5,giugno:6,luglio:7,agosto:8,settembre:9,ottobre:10,novembre:11,dicembre:12};
|
|
3185
|
+
const dateMatch = travelTask.match(/(\d{1,2})\s+(gennaio|febbraio|marzo|aprile|maggio|giugno|luglio|agosto|settembre|ottobre|novembre|dicembre)/i)
|
|
3186
|
+
|| travelTask.match(/(\d{1,2})[\/\-](\d{1,2})(?:[\/\-](\d{4}))?/);
|
|
3187
3187
|
let targetDate = null;
|
|
3188
3188
|
let targetDateStr = null;
|
|
3189
|
-
if (dateMatch
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3189
|
+
if (dateMatch) {
|
|
3190
|
+
if (dateMatch[2] && isNaN(Number(dateMatch[2]))) {
|
|
3191
|
+
const day = parseInt(dateMatch[1]);
|
|
3192
|
+
const month = monthNames[(dateMatch[2] || '').toLowerCase()];
|
|
3193
|
+
if (day && month) {
|
|
3194
|
+
const year = new Date().getFullYear();
|
|
3195
|
+
targetDate = new Date(year, month - 1, day);
|
|
3196
|
+
targetDateStr = String(day).padStart(2, '0') + '/' + String(month).padStart(2, '0') + '/' + year;
|
|
3197
|
+
}
|
|
3198
|
+
} else if (dateMatch[1] && dateMatch[2]) {
|
|
3199
|
+
const day = parseInt(dateMatch[1]);
|
|
3200
|
+
const month = parseInt(dateMatch[2]);
|
|
3201
|
+
const year = dateMatch[3] ? parseInt(dateMatch[3]) : new Date().getFullYear();
|
|
3202
|
+
if (day >= 1 && day <= 31 && month >= 1 && month <= 12) {
|
|
3203
|
+
targetDate = new Date(year, month - 1, day);
|
|
3204
|
+
targetDateStr = String(day).padStart(2, '0') + '/' + String(month).padStart(2, '0') + '/' + year;
|
|
3205
|
+
}
|
|
3196
3206
|
}
|
|
3197
3207
|
}
|
|
3198
3208
|
|
|
3199
|
-
// City
|
|
3200
|
-
const cityMatch = travelTask.match(/\b(?:a|in|near|vicino\s+a|at)\s+([A-
|
|
3201
|
-
const city = cityMatch ? cityMatch[1].trim() : null;
|
|
3209
|
+
// City: "a Mantova", "in Bologna", "near Milan", "vicino a Firenze"
|
|
3210
|
+
const cityMatch = travelTask.match(/\b(?:a|in|near|vicino\s+a|at|around)\s+([A-Za-z][a-zA-Z\s]{2,25}?)(?:\s*[,\.!?\n]|\s+(?:il|la|per|con|un|una|mi|mia|del|della|per\s+la|il\s+|la\s+)|$)/i);
|
|
3211
|
+
const city = cityMatch ? cityMatch[1].trim().replace(/\s+$/, '') : null;
|
|
3202
3212
|
|
|
3203
|
-
// Cuisine
|
|
3204
|
-
const
|
|
3205
|
-
const
|
|
3213
|
+
// Cuisine type → OSM cuisine tag
|
|
3214
|
+
const cuisineMap = {sushi:'sushi',pizza:'pizza',giapponese:'japanese',japanese:'japanese',italiano:'italian',italian:'italian',cinese:'chinese',chinese:'chinese',indiano:'indian',indian:'indian',steakhouse:'steak_house',trattoria:'italian',osteria:'italian'};
|
|
3215
|
+
const cuisineMatch = travelTask.match(/\b(sushi|pizza|giapponese|japanese|italiano|italian|cinese|chinese|indiano|indian|steakhouse|trattoria|osteria)\b/i);
|
|
3216
|
+
const cuisineRaw = cuisineMatch ? cuisineMatch[1].toLowerCase() : null;
|
|
3217
|
+
const cuisineOSM = cuisineRaw ? (cuisineMap[cuisineRaw] || cuisineRaw) : null;
|
|
3206
3218
|
|
|
3207
|
-
|
|
3208
|
-
const hasAccommodation = /b&b|bed\s*&?\s*breakfast|hotel|albergo|agriturismo|locanda|ostello|hostel|airbnb/i.test(travelTask);
|
|
3219
|
+
const hasAccommodation = /b&b|bed\s*(?:&|and)\s*breakfast|hotel|albergo|agriturismo|locanda|ostello|hostel|pernottament|soggiorno/i.test(travelTask);
|
|
3209
3220
|
const hasRestaurant = /ristorante|restaurant|sushi|cena|dinner|pranzo|lunch|mangiare|eat/i.test(travelTask);
|
|
3210
3221
|
|
|
3211
3222
|
const summaryParts = [];
|
|
3212
|
-
if (city) summaryParts.push('
|
|
3223
|
+
if (city) summaryParts.push('city: ' + city);
|
|
3213
3224
|
if (targetDateStr) summaryParts.push('date: ' + targetDateStr);
|
|
3214
|
-
if (
|
|
3225
|
+
if (cuisineRaw) summaryParts.push('cuisine: ' + cuisineRaw);
|
|
3215
3226
|
if (hasAccommodation) summaryParts.push('accommodation: yes');
|
|
3216
3227
|
if (hasRestaurant) summaryParts.push('restaurant: yes');
|
|
3217
3228
|
sendToken('[Extracted — ' + (summaryParts.join(', ') || 'general search') + '] ');
|
|
3218
3229
|
|
|
3219
|
-
|
|
3230
|
+
const UA = 'NHA-TravelAgent/1.0 (nothumanallowed.com)';
|
|
3220
3231
|
const portalResults = [];
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
if (hasRestaurant) {
|
|
3227
|
-
const theforkQuery = [cuisine, city].filter(Boolean).join(' ');
|
|
3228
|
-
const theforkUrl = 'https://www.thefork.it/ristoranti/' + (city ? encodeURIComponent(city.toLowerCase()) : 'italia') + (cuisine ? '?q=' + cuisineQ : '');
|
|
3229
|
-
sendToken('[Searching TheFork...] ');
|
|
3232
|
+
|
|
3233
|
+
// ── TIER 1: Nominatim geocoding → get lat/lon for city ──
|
|
3234
|
+
let lat = null, lon = null;
|
|
3235
|
+
if (city) {
|
|
3236
|
+
sendToken('[Geocoding ' + city + '...] ');
|
|
3230
3237
|
try {
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3238
|
+
const geoUrl = 'https://nominatim.openstreetmap.org/search?' + new URLSearchParams({ q: city, format: 'json', limit: '1', addressdetails: '0' }).toString();
|
|
3239
|
+
const geoRes = await withTimeout(fetch(geoUrl, { headers: { 'User-Agent': UA, 'Accept': 'application/json' } }), 8000);
|
|
3240
|
+
if (geoRes.ok) {
|
|
3241
|
+
const geoData = await geoRes.json();
|
|
3242
|
+
if (geoData && geoData[0]) {
|
|
3243
|
+
lat = parseFloat(geoData[0].lat);
|
|
3244
|
+
lon = parseFloat(geoData[0].lon);
|
|
3245
|
+
sendToken('[Found: ' + lat.toFixed(4) + ', ' + lon.toFixed(4) + '] ');
|
|
3246
|
+
}
|
|
3240
3247
|
}
|
|
3241
|
-
} catch
|
|
3248
|
+
} catch {}
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
// ── TIER 1: Overpass API — real OSM data ──
|
|
3252
|
+
if (lat !== null && lon !== null) {
|
|
3253
|
+
const radius = 5000; // 5km radius
|
|
3254
|
+
const formatPlace = (el) => {
|
|
3255
|
+
const t = el.tags || {};
|
|
3256
|
+
const parts = [];
|
|
3257
|
+
if (t.name) parts.push('**' + t.name + '**');
|
|
3258
|
+
if (t['addr:street']) parts.push(t['addr:street'] + (t['addr:housenumber'] ? ' ' + t['addr:housenumber'] : ''));
|
|
3259
|
+
if (t['addr:city']) parts.push(t['addr:city']);
|
|
3260
|
+
if (t.phone || t['contact:phone']) parts.push('Tel: ' + (t.phone || t['contact:phone']));
|
|
3261
|
+
if (t.website || t['contact:website']) parts.push('Web: ' + (t.website || t['contact:website']));
|
|
3262
|
+
if (t.opening_hours) parts.push('Orari: ' + t.opening_hours);
|
|
3263
|
+
if (t.email || t['contact:email']) parts.push('Email: ' + (t.email || t['contact:email']));
|
|
3264
|
+
if (t.cuisine) parts.push('Cucina: ' + t.cuisine);
|
|
3265
|
+
if (t.stars) parts.push('Stelle: ' + t.stars);
|
|
3266
|
+
const coordLat = el.lat || (el.center && el.center.lat);
|
|
3267
|
+
const coordLon = el.lon || (el.center && el.center.lon);
|
|
3268
|
+
if (coordLat && coordLon) parts.push('Maps: https://www.openstreetmap.org/?mlat=' + coordLat + '&mlon=' + coordLon + '&zoom=17');
|
|
3269
|
+
return parts.join(' | ');
|
|
3270
|
+
};
|
|
3271
|
+
|
|
3272
|
+
// Restaurants query
|
|
3273
|
+
if (hasRestaurant) {
|
|
3274
|
+
sendToken('[Overpass: searching restaurants' + (cuisineOSM ? ' (' + cuisineOSM + ')' : '') + '...] ');
|
|
3242
3275
|
try {
|
|
3243
|
-
const
|
|
3244
|
-
|
|
3245
|
-
|
|
3276
|
+
const cuisineFilter = cuisineOSM ? '["cuisine"="' + cuisineOSM + '"]' : '';
|
|
3277
|
+
const restQuery = '[out:json][timeout:20];(node["amenity"="restaurant"]' + cuisineFilter + '(around:' + radius + ',' + lat + ',' + lon + ');way["amenity"="restaurant"]' + cuisineFilter + '(around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
|
|
3278
|
+
const restRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
|
|
3279
|
+
method: 'POST',
|
|
3280
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
|
|
3281
|
+
body: 'data=' + encodeURIComponent(restQuery)
|
|
3282
|
+
}), 25000);
|
|
3283
|
+
if (restRes.ok) {
|
|
3284
|
+
const restData = await restRes.json();
|
|
3285
|
+
const elements = (restData.elements || []).filter(el => el.tags && el.tags.name);
|
|
3286
|
+
if (elements.length > 0) {
|
|
3287
|
+
const lines = ['## Ristoranti trovati via OpenStreetMap (' + elements.length + ' risultati, raggio ' + (radius/1000) + 'km da ' + city + ')'];
|
|
3288
|
+
elements.slice(0, 15).forEach((el, i) => { lines.push((i+1) + '. ' + formatPlace(el)); });
|
|
3289
|
+
portalResults.push(lines.join('\n'));
|
|
3290
|
+
sendToken('[Found ' + elements.length + ' restaurants] ');
|
|
3291
|
+
} else if (cuisineOSM) {
|
|
3292
|
+
// Retry without cuisine filter — broader search
|
|
3293
|
+
sendToken('[No ' + cuisineOSM + ' found, retrying broader...] ');
|
|
3294
|
+
const broadQuery = '[out:json][timeout:20];(node["amenity"="restaurant"](around:' + radius + ',' + lat + ',' + lon + ');way["amenity"="restaurant"](around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
|
|
3295
|
+
const broadRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
|
|
3296
|
+
method: 'POST',
|
|
3297
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
|
|
3298
|
+
body: 'data=' + encodeURIComponent(broadQuery)
|
|
3299
|
+
}), 25000);
|
|
3300
|
+
if (broadRes.ok) {
|
|
3301
|
+
const broadData = await broadRes.json();
|
|
3302
|
+
const broadEl = (broadData.elements || []).filter(el => el.tags && el.tags.name);
|
|
3303
|
+
if (broadEl.length > 0) {
|
|
3304
|
+
const lines = ['## Ristoranti a ' + city + ' (tutti i tipi, ' + broadEl.length + ' trovati — nessun ristorante ' + cuisineRaw + ' su OpenStreetMap in questa zona)'];
|
|
3305
|
+
broadEl.slice(0, 10).forEach((el, i) => { lines.push((i+1) + '. ' + formatPlace(el)); });
|
|
3306
|
+
portalResults.push(lines.join('\n'));
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
} catch (e) { sendToken('[Overpass restaurants error: ' + e.message.slice(0,50) + '] '); }
|
|
3246
3312
|
}
|
|
3247
|
-
}
|
|
3248
3313
|
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
const checkout = targetDate ? new Date(targetDate.getTime() + 86400000).toISOString().slice(0, 10) : '';
|
|
3253
|
-
const bookingUrl = 'https://www.booking.com/search.html?ss=' + (cityQ || 'Italy') + (checkin ? '&checkin=' + checkin + '&checkout=' + checkout : '') + '&group_adults=2&no_rooms=1&b_h4u_keep_filters=&from_sf=1';
|
|
3254
|
-
sendToken('[Searching Booking.com...] ');
|
|
3255
|
-
try {
|
|
3256
|
-
if (!be.isBrowserRunning()) await be.browserOpen(bookingUrl, { waitForLoad: true });
|
|
3257
|
-
else await be.browserNavigate(bookingUrl, { waitForLoad: true });
|
|
3258
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
3259
|
-
const bookingContent = await be.browserExtract({ selector: 'body', format: 'text' });
|
|
3260
|
-
if (bookingContent && !bookingContent.error && bookingContent.text && bookingContent.text.length > 200) {
|
|
3261
|
-
portalResults.push('## Booking.com — ' + bookingUrl + '\n' + bookingContent.text.slice(0, 5000));
|
|
3262
|
-
} else {
|
|
3263
|
-
const fetchBook = await withTimeout(executeTool('fetch_url', { url: bookingUrl }, config), 20000);
|
|
3264
|
-
if (fetchBook && fetchBook.length > 200) portalResults.push('## Booking.com — ' + bookingUrl + '\n' + fetchBook.slice(0, 5000));
|
|
3265
|
-
}
|
|
3266
|
-
} catch (e) {
|
|
3314
|
+
// Accommodation query
|
|
3315
|
+
if (hasAccommodation) {
|
|
3316
|
+
sendToken('[Overpass: searching accommodation...] ');
|
|
3267
3317
|
try {
|
|
3268
|
-
const
|
|
3269
|
-
|
|
3270
|
-
|
|
3318
|
+
const accQuery = '[out:json][timeout:20];(node["tourism"~"hotel|guest_house|bed_and_breakfast|hostel|motel"](around:' + radius + ',' + lat + ',' + lon + ');way["tourism"~"hotel|guest_house|bed_and_breakfast|hostel|motel"](around:' + radius + ',' + lat + ',' + lon + ');node["amenity"="hotel"](around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
|
|
3319
|
+
const accRes = await withTimeout(fetch('https://overpass.kumi.systems/api/interpreter', {
|
|
3320
|
+
method: 'POST',
|
|
3321
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': UA },
|
|
3322
|
+
body: 'data=' + encodeURIComponent(accQuery)
|
|
3323
|
+
}), 25000);
|
|
3324
|
+
if (accRes.ok) {
|
|
3325
|
+
const accData = await accRes.json();
|
|
3326
|
+
const elements = (accData.elements || []).filter(el => el.tags && el.tags.name);
|
|
3327
|
+
if (elements.length > 0) {
|
|
3328
|
+
const lines = ['## Alloggi trovati via OpenStreetMap (' + elements.length + ' risultati, raggio ' + (radius/1000) + 'km da ' + city + ')'];
|
|
3329
|
+
elements.slice(0, 15).forEach((el, i) => {
|
|
3330
|
+
const t = el.tags || {};
|
|
3331
|
+
const type = t.tourism === 'guest_house' ? 'B&B/Guest house' : t.tourism === 'hotel' ? 'Hotel' : t.tourism === 'bed_and_breakfast' ? 'B&B' : (t.tourism || 'Alloggio');
|
|
3332
|
+
lines.push((i+1) + '. [' + type + '] ' + formatPlace(el));
|
|
3333
|
+
});
|
|
3334
|
+
portalResults.push(lines.join('\n'));
|
|
3335
|
+
sendToken('[Found ' + elements.length + ' accommodations] ');
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
} catch (e) { sendToken('[Overpass accommodation error: ' + e.message.slice(0,50) + '] '); }
|
|
3271
3339
|
}
|
|
3340
|
+
} else if (city) {
|
|
3341
|
+
sendToken('[Geocoding failed, skipping Overpass] ');
|
|
3272
3342
|
}
|
|
3273
3343
|
|
|
3274
|
-
//
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
sendToken('[
|
|
3344
|
+
// ── TIER 2: Web search fallback — targeted queries ──
|
|
3345
|
+
const dataFound = portalResults.length > 0 && portalResults.some(r => r.includes('trovati') && !r.includes('0 risultati'));
|
|
3346
|
+
if (!dataFound) {
|
|
3347
|
+
sendToken('[Web search fallback...] ');
|
|
3278
3348
|
try {
|
|
3279
|
-
const
|
|
3280
|
-
|
|
3349
|
+
const queries = [
|
|
3350
|
+
[cuisineRaw, 'ristorante', city].filter(Boolean).join(' '),
|
|
3351
|
+
hasAccommodation ? ['b&b romantico', city, targetDateStr ? targetDateStr.slice(0,5) : ''].filter(Boolean).join(' ') : null
|
|
3352
|
+
].filter(Boolean);
|
|
3353
|
+
for (const q of queries) {
|
|
3354
|
+
const sr = await withTimeout(executeTool('web_search', { query: q, deep: true }, config), 30000);
|
|
3355
|
+
if (sr && sr.length > 100) portalResults.push('## Web search: "' + q + '"\n' + (typeof sr === 'string' ? sr : JSON.stringify(sr)));
|
|
3356
|
+
}
|
|
3281
3357
|
} catch {}
|
|
3282
3358
|
}
|
|
3283
3359
|
|
|
3284
|
-
//
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
}
|
|
3292
|
-
} catch {}
|
|
3293
|
-
|
|
3294
|
-
toolData = portalResults.length > 0
|
|
3295
|
-
? portalResults.join('\n\n')
|
|
3296
|
-
: 'No results found on travel portals. Try specifying city, date, and type of place.';
|
|
3360
|
+
// ── Build final output ──
|
|
3361
|
+
if (portalResults.length > 0) {
|
|
3362
|
+
const header = '# TravelAgent — Risultati per: ' + (city || 'zona non specificata') + (targetDateStr ? ' | Data: ' + targetDateStr : '') + (cuisineRaw ? ' | Cucina: ' + cuisineRaw : '') + '\n\n';
|
|
3363
|
+
toolData = header + portalResults.join('\n\n');
|
|
3364
|
+
} else {
|
|
3365
|
+
toolData = '# TravelAgent — Nessun risultato trovato\n\nNon sono stati trovati ristoranti o alloggi per i parametri specificati.\n\n**Parametri cercati:**\n- Città: ' + (city || 'non specificata') + '\n- Data: ' + (targetDateStr || 'non specificata') + '\n- Tipo cucina: ' + (cuisineRaw || 'qualsiasi') + '\n\n**Suggerimento:** Specifica la città esatta (es. "a Mantova") e il tipo di cucina per ottenere risultati migliori.';
|
|
3366
|
+
}
|
|
3297
3367
|
} catch (e) { toolData = toolData || 'TravelAgent failed: ' + e.message; }
|
|
3298
3368
|
|
|
3299
3369
|
} else if (agent === 'BrowserAgent') {
|
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 = '13.5.
|
|
8
|
+
export const VERSION = '13.5.106';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|