nothumanallowed 13.5.176 → 13.5.178
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 +41 -0
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +69 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.178",
|
|
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
|
@@ -694,6 +694,47 @@ export async function cmdUI(args) {
|
|
|
694
694
|
return;
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
+
// GET /api/weather?location=<city> — live weather via wttr.in (no API key)
|
|
698
|
+
if (method === 'GET' && pathname === '/api/weather') {
|
|
699
|
+
const loc = (url.searchParams.get('location') || config.location || '').trim();
|
|
700
|
+
if (!loc) { sendJSON(res, 400, { error: 'location required' }); return; }
|
|
701
|
+
try {
|
|
702
|
+
const wttrRes = await fetch(`https://wttr.in/${encodeURIComponent(loc)}?format=j1`, {
|
|
703
|
+
headers: { 'User-Agent': 'nha-cli/1.0' },
|
|
704
|
+
signal: AbortSignal.timeout(8000),
|
|
705
|
+
});
|
|
706
|
+
if (!wttrRes.ok) { sendJSON(res, 502, { error: `wttr.in ${wttrRes.status}` }); return; }
|
|
707
|
+
const w = await wttrRes.json();
|
|
708
|
+
const cur = w.current_condition?.[0];
|
|
709
|
+
const area = w.nearest_area?.[0];
|
|
710
|
+
if (!cur) { sendJSON(res, 404, { error: 'No weather data' }); return; }
|
|
711
|
+
const forecast = (w.weather || []).slice(0, 3).map(d => ({
|
|
712
|
+
date: d.date,
|
|
713
|
+
maxC: d.maxtempC,
|
|
714
|
+
minC: d.mintempC,
|
|
715
|
+
desc: d.hourly?.[4]?.weatherDesc?.[0]?.value || '',
|
|
716
|
+
rain: d.hourly?.[4]?.chanceofrain || '0',
|
|
717
|
+
}));
|
|
718
|
+
sendJSON(res, 200, {
|
|
719
|
+
city: area?.areaName?.[0]?.value || loc,
|
|
720
|
+
country: area?.country?.[0]?.value || '',
|
|
721
|
+
tempC: cur.temp_C,
|
|
722
|
+
feelsC: cur.FeelsLikeC,
|
|
723
|
+
humidity: cur.humidity,
|
|
724
|
+
windKmph: cur.windspeedKmph,
|
|
725
|
+
windDir: cur.winddir16Point,
|
|
726
|
+
uvIndex: cur.uvIndex,
|
|
727
|
+
desc: cur.weatherDesc?.[0]?.value || '',
|
|
728
|
+
cloudcover: cur.cloudcover,
|
|
729
|
+
forecast,
|
|
730
|
+
});
|
|
731
|
+
} catch (e) {
|
|
732
|
+
sendJSON(res, 502, { error: e.message });
|
|
733
|
+
}
|
|
734
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
697
738
|
// GET /api/status
|
|
698
739
|
if (method === 'GET' && pathname === '/api/status') {
|
|
699
740
|
const { loadTokens: loadTokSt } = await import('../services/token-store.mjs');
|
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.178';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -11,8 +11,8 @@ var currentView = 'dashboard';
|
|
|
11
11
|
var chatHistory = [];
|
|
12
12
|
var activeConvId = null;
|
|
13
13
|
var convList = [];
|
|
14
|
-
var dash = {emails:[],events:[],tasks:[],plan:null,status:null};
|
|
15
|
-
var dashLoaded = {emails:false,events:false,tasks:false,contacts:false,notes:false,drive:false,github:false,notion:false,slack:false};
|
|
14
|
+
var dash = {emails:[],events:[],tasks:[],plan:null,status:null,weather:null};
|
|
15
|
+
var dashLoaded = {emails:false,events:false,tasks:false,contacts:false,notes:false,drive:false,github:false,notion:false,slack:false,weather:false};
|
|
16
16
|
var chatStreaming = false;
|
|
17
17
|
var chatAbortController = null;
|
|
18
18
|
|
|
@@ -286,11 +286,46 @@ function apiPost(p,b,m){return fetch(API+p,{method:m||'POST',headers:{'Content-T
|
|
|
286
286
|
function apiPatch(p){return fetch(API+p,{method:'PATCH'}).then(function(r){return r.ok?r.json():null}).catch(function(){return null})}
|
|
287
287
|
|
|
288
288
|
// ---- LOAD DATA ----
|
|
289
|
+
function loadWeather(){
|
|
290
|
+
var savedLoc=localStorage.getItem('nha_weather_location');
|
|
291
|
+
function fetchWeather(loc){
|
|
292
|
+
apiGet('/api/weather?location='+encodeURIComponent(loc)).then(function(r){
|
|
293
|
+
if(r&&r.tempC){dash.weather=r;dashLoaded.weather=true;if(currentView==='dashboard')render();}
|
|
294
|
+
else{dashLoaded.weather=true;if(currentView==='dashboard')render();}
|
|
295
|
+
}).catch(function(){dashLoaded.weather=true;if(currentView==='dashboard')render();});
|
|
296
|
+
}
|
|
297
|
+
function ipFallback(){
|
|
298
|
+
fetch('https://ipapi.co/json/').then(function(r){return r.json();}).then(function(d){
|
|
299
|
+
var city=d.city||'';
|
|
300
|
+
if(city){localStorage.setItem('nha_weather_location',city);fetchWeather(city);}
|
|
301
|
+
else{dashLoaded.weather=true;render();}
|
|
302
|
+
}).catch(function(){dashLoaded.weather=true;render();});
|
|
303
|
+
}
|
|
304
|
+
// If location already known, use it directly
|
|
305
|
+
if(savedLoc){fetchWeather(savedLoc);return;}
|
|
306
|
+
// Try browser geolocation — pass lat,lng directly to wttr.in (no reverse geocoding needed)
|
|
307
|
+
if(navigator.geolocation){
|
|
308
|
+
navigator.geolocation.getCurrentPosition(function(pos){
|
|
309
|
+
var latLng=pos.coords.latitude.toFixed(4)+','+pos.coords.longitude.toFixed(4);
|
|
310
|
+
fetchWeather(latLng);
|
|
311
|
+
// Also resolve city name for display — save for next time
|
|
312
|
+
fetch('https://nominatim.openstreetmap.org/reverse?lat='+pos.coords.latitude+'&lon='+pos.coords.longitude+'&format=json&accept-language=en')
|
|
313
|
+
.then(function(r){return r.json();}).then(function(d){
|
|
314
|
+
var city=d.address&&(d.address.city||d.address.town||d.address.village||d.address.county||'');
|
|
315
|
+
if(city)localStorage.setItem('nha_weather_location',city);
|
|
316
|
+
}).catch(function(){});
|
|
317
|
+
},function(){ipFallback();},{timeout:6000,maximumAge:300000});
|
|
318
|
+
} else {
|
|
319
|
+
ipFallback();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
289
323
|
function loadDash(){
|
|
290
324
|
// Load each API independently - render as each arrives (emails are slow)
|
|
291
325
|
apiGet('/api/status').then(function(r){dash.status=r;render()});
|
|
292
326
|
apiGet('/api/tasks').then(function(r){dash.tasks=(r&&r.tasks)||[];dashLoaded.tasks=true;updateBadges();render()});
|
|
293
327
|
apiGet('/api/calendar').then(function(r){dash.events=(r&&r.events)||[];dashLoaded.events=true;updateBadges();render()});
|
|
328
|
+
if(!dashLoaded.weather)loadWeather();
|
|
294
329
|
return apiGet('/api/emails?page=0&pageSize=25').then(function(r){dash.emails=(r&&r.emails)||[];dash._emailHasMore=r&&r.hasMore;dashLoaded.emails=true;emailPage=0;updateBadges();render()});
|
|
295
330
|
}
|
|
296
331
|
function loadAgents(){return apiGet('/api/agents').then(function(r){agentsList=(r&&r.agents)||[]})}
|
|
@@ -358,11 +393,28 @@ function renderDash(el){
|
|
|
358
393
|
var done=t.filter(function(x){return x.status==='done'}).length;
|
|
359
394
|
var pend=t.length-done;
|
|
360
395
|
var pct=t.length>0?Math.round(done/t.length*100):0;
|
|
396
|
+
var weatherCard;
|
|
397
|
+
if(dashLoaded.weather&&dash.weather){
|
|
398
|
+
var wm=dash.weather;
|
|
399
|
+
var wIcons={Sunny:'☀',Clear:'☀','Partly Cloudy':'⛅','Partly cloudy':'⛅',Cloudy:'☁',Overcast:'☁','Light rain':'🌧','Patchy light drizzle':'🌧','Moderate rain':'🌧','Heavy rain':'🌧',Rain:'🌧',Drizzle:'🌧',Snow:'❄','Light snow':'❄',Fog:'🌫',Mist:'🌫',Thunder:'⚡','Thundery outbreaks':'⚡'};
|
|
400
|
+
var wIcon=wIcons[wm.desc]||'🌥';
|
|
401
|
+
weatherCard='<div class="card" style="cursor:default"><div class="card__title" style="display:flex;align-items:center;justify-content:space-between">'+
|
|
402
|
+
'<span>'+esc(wm.city)+(wm.country?', '+esc(wm.country.slice(0,2)):'')+'</span>'+
|
|
403
|
+
'<span style="font-size:10px;color:var(--dim);cursor:pointer" onclick="nhaSetWeatherLocation()">✎ change</span>'+
|
|
404
|
+
'</div>'+
|
|
405
|
+
'<div class="card__value" style="font-size:28px">'+wIcon+' '+esc(wm.tempC)+'°C</div>'+
|
|
406
|
+
'<div class="card__sub">'+esc(wm.desc)+' · feels '+esc(wm.feelsC)+'°C · 💧'+esc(wm.humidity)+'%</div>'+
|
|
407
|
+
'</div>';
|
|
408
|
+
} else {
|
|
409
|
+
weatherCard='<div class="card" style="cursor:default"><div class="card__title" style="display:flex;align-items:center;justify-content:space-between"><span>Weather</span><span style="font-size:10px;color:var(--dim);cursor:pointer" onclick="nhaSetWeatherLocation()">✎ set location</span></div>'+
|
|
410
|
+
'<div class="card__value" style="font-size:22px">--</div>'+
|
|
411
|
+
'<div class="card__sub">'+(dashLoaded.weather?'No data':'Loading...')+'</div></div>';
|
|
412
|
+
}
|
|
361
413
|
var h='<div class="dash-grid">'+
|
|
362
414
|
'<div class="card"><div class="card__title">Tasks</div><div class="card__value">'+pend+'</div><div class="card__sub">'+done+'/'+t.length+' done ('+pct+'%)</div></div>'+
|
|
363
415
|
'<div class="card"><div class="card__title">Emails</div><div class="card__value">'+(dashLoaded.emails?e.length:'<span class="spinner" style="width:14px;height:14px;display:inline-block;vertical-align:middle"></span>')+'</div><div class="card__sub">'+(dashLoaded.emails?(e.length>0?esc(e[0].from):'Inbox zero'):'Loading...')+'</div></div>'+
|
|
364
416
|
'<div class="card"><div class="card__title">Events</div><div class="card__value">'+ev.length+'</div><div class="card__sub">'+(ev.length>0?esc(ev[0].summary):'No events')+'</div></div>'+
|
|
365
|
-
|
|
417
|
+
weatherCard+
|
|
366
418
|
'</div>';
|
|
367
419
|
if(ev.length>0){h+='<div class="section-title">Events</div>';ev.slice(0,5).forEach(function(x){h+='<div class="card event"><span class="event__time">'+(x.isAllDay?'All day':fmtTime(x.start)+' - '+fmtTime(x.end))+'</span><span class="event__title">'+esc(x.summary)+'</span>'+(x.location?'<span class="event__location">'+esc(x.location)+'</span>':'')+'</div>'})}
|
|
368
420
|
if(e.length>0){h+='<div class="section-title">Emails</div>';e.slice(0,5).forEach(function(x){h+='<div class="card email"><div class="email__header"><span class="email__from">'+esc(x.from)+'</span><span class="email__date">'+esc(x.date)+'</span></div><div class="email__subject">'+esc(x.subject)+'</div><div class="email__snippet">'+esc((x.snippet||'').slice(0,120))+'</div></div>'})}
|
|
@@ -3830,6 +3882,20 @@ function handleDaemonEvent(msg) {
|
|
|
3830
3882
|
}
|
|
3831
3883
|
}
|
|
3832
3884
|
|
|
3885
|
+
function nhaSetWeatherLocation() {
|
|
3886
|
+
var current = localStorage.getItem('nha_weather_location') || '';
|
|
3887
|
+
var city = prompt('Enter your city for weather (e.g. "Rome", "Viterbo, Italy"):', current);
|
|
3888
|
+
if (city === null) return;
|
|
3889
|
+
city = city.trim();
|
|
3890
|
+
if (!city) { localStorage.removeItem('nha_weather_location'); dash.weather = null; dashLoaded.weather = false; render(); return; }
|
|
3891
|
+
localStorage.setItem('nha_weather_location', city);
|
|
3892
|
+
dash.weather = null; dashLoaded.weather = false;
|
|
3893
|
+
render();
|
|
3894
|
+
apiGet('/api/weather?location=' + encodeURIComponent(city)).then(function(r) {
|
|
3895
|
+
if (r && r.tempC) { dash.weather = r; dashLoaded.weather = true; render(); }
|
|
3896
|
+
}).catch(function() {});
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3833
3899
|
function npmUpdate() {
|
|
3834
3900
|
var existing = document.getElementById('npmUpdateModal');
|
|
3835
3901
|
if (existing) existing.remove();
|