nothumanallowed 13.5.176 → 13.5.177

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": "13.5.176",
3
+ "version": "13.5.177",
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": {
@@ -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 = (urlObj.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.176';
8
+ export const VERSION = '13.5.177';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -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,43 @@ 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
+ // Try browser geolocation first, fall back to IP geolocation, then saved config
291
+ var savedLoc=localStorage.getItem('nha_weather_location');
292
+ function fetchWeather(loc){
293
+ apiGet('/api/weather?location='+encodeURIComponent(loc)).then(function(r){
294
+ if(r&&r.tempC){dash.weather=r;dashLoaded.weather=true;if(currentView==='dashboard')render();}
295
+ }).catch(function(){});
296
+ }
297
+ if(savedLoc){fetchWeather(savedLoc);return;}
298
+ if(navigator.geolocation){
299
+ navigator.geolocation.getCurrentPosition(function(pos){
300
+ // Reverse geocode via nominatim (free, no key)
301
+ fetch('https://nominatim.openstreetmap.org/reverse?lat='+pos.coords.latitude+'&lon='+pos.coords.longitude+'&format=json',{headers:{'User-Agent':'nha-cli/1.0'}})
302
+ .then(function(r){return r.json();})
303
+ .then(function(d){
304
+ var city=d.address&&(d.address.city||d.address.town||d.address.village||d.address.county||'');
305
+ if(city){localStorage.setItem('nha_weather_location',city);fetchWeather(city);}
306
+ }).catch(function(){});
307
+ },function(){
308
+ // Geoloc denied — IP fallback
309
+ fetch('https://ipapi.co/json/').then(function(r){return r.json();}).then(function(d){
310
+ var city=d.city||'';if(city){localStorage.setItem('nha_weather_location',city);fetchWeather(city);}
311
+ }).catch(function(){});
312
+ },{timeout:5000});
313
+ } else {
314
+ fetch('https://ipapi.co/json/').then(function(r){return r.json();}).then(function(d){
315
+ var city=d.city||'';if(city){localStorage.setItem('nha_weather_location',city);fetchWeather(city);}
316
+ }).catch(function(){});
317
+ }
318
+ }
319
+
289
320
  function loadDash(){
290
321
  // Load each API independently - render as each arrives (emails are slow)
291
322
  apiGet('/api/status').then(function(r){dash.status=r;render()});
292
323
  apiGet('/api/tasks').then(function(r){dash.tasks=(r&&r.tasks)||[];dashLoaded.tasks=true;updateBadges();render()});
293
324
  apiGet('/api/calendar').then(function(r){dash.events=(r&&r.events)||[];dashLoaded.events=true;updateBadges();render()});
325
+ if(!dashLoaded.weather)loadWeather();
294
326
  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
327
  }
296
328
  function loadAgents(){return apiGet('/api/agents').then(function(r){agentsList=(r&&r.agents)||[]})}
@@ -358,11 +390,28 @@ function renderDash(el){
358
390
  var done=t.filter(function(x){return x.status==='done'}).length;
359
391
  var pend=t.length-done;
360
392
  var pct=t.length>0?Math.round(done/t.length*100):0;
393
+ var weatherCard;
394
+ if(dashLoaded.weather&&dash.weather){
395
+ var wm=dash.weather;
396
+ var wIcons={Sunny:'&#9728;',Clear:'&#9728;','Partly Cloudy':'&#9925;','Partly cloudy':'&#9925;',Cloudy:'&#9729;',Overcast:'&#9729;','Light rain':'&#127783;','Patchy light drizzle':'&#127783;','Moderate rain':'&#127783;','Heavy rain':'&#127783;',Rain:'&#127783;',Drizzle:'&#127783;',Snow:'&#10052;','Light snow':'&#10052;',Fog:'&#127787;',Mist:'&#127787;',Thunder:'&#9889;','Thundery outbreaks':'&#9889;'};
397
+ var wIcon=wIcons[wm.desc]||'&#127781;';
398
+ weatherCard='<div class="card" style="cursor:default"><div class="card__title" style="display:flex;align-items:center;justify-content:space-between">'+
399
+ '<span>'+esc(wm.city)+(wm.country?', '+esc(wm.country.slice(0,2)):'')+'</span>'+
400
+ '<span style="font-size:10px;color:var(--dim);cursor:pointer" onclick="nhaSetWeatherLocation()">&#9998; change</span>'+
401
+ '</div>'+
402
+ '<div class="card__value" style="font-size:28px">'+wIcon+' '+esc(wm.tempC)+'&#176;C</div>'+
403
+ '<div class="card__sub">'+esc(wm.desc)+' &middot; feels '+esc(wm.feelsC)+'&#176;C &middot; &#128167;'+esc(wm.humidity)+'%</div>'+
404
+ '</div>';
405
+ } else {
406
+ 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()">&#9998; set location</span></div>'+
407
+ '<div class="card__value" style="font-size:22px">--</div>'+
408
+ '<div class="card__sub">'+(dashLoaded.weather?'No data':'Loading...')+'</div></div>';
409
+ }
361
410
  var h='<div class="dash-grid">'+
362
411
  '<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
412
  '<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
413
  '<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
- '<div class="card"><div class="card__title">Agents</div><div class="card__value">38</div><div class="card__sub">Ready</div></div>'+
414
+ weatherCard+
366
415
  '</div>';
367
416
  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
417
  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 +3879,20 @@ function handleDaemonEvent(msg) {
3830
3879
  }
3831
3880
  }
3832
3881
 
3882
+ function nhaSetWeatherLocation() {
3883
+ var current = localStorage.getItem('nha_weather_location') || '';
3884
+ var city = prompt('Enter your city for weather (e.g. "Rome", "Viterbo, Italy"):', current);
3885
+ if (city === null) return;
3886
+ city = city.trim();
3887
+ if (!city) { localStorage.removeItem('nha_weather_location'); dash.weather = null; dashLoaded.weather = false; render(); return; }
3888
+ localStorage.setItem('nha_weather_location', city);
3889
+ dash.weather = null; dashLoaded.weather = false;
3890
+ render();
3891
+ apiGet('/api/weather?location=' + encodeURIComponent(city)).then(function(r) {
3892
+ if (r && r.tempC) { dash.weather = r; dashLoaded.weather = true; render(); }
3893
+ }).catch(function() {});
3894
+ }
3895
+
3833
3896
  function npmUpdate() {
3834
3897
  var existing = document.getElementById('npmUpdateModal');
3835
3898
  if (existing) existing.remove();