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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.5.176",
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": {
@@ -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.176';
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
 
@@ -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:'&#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;'};
400
+ var wIcon=wIcons[wm.desc]||'&#127781;';
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()">&#9998; change</span>'+
404
+ '</div>'+
405
+ '<div class="card__value" style="font-size:28px">'+wIcon+' '+esc(wm.tempC)+'&#176;C</div>'+
406
+ '<div class="card__sub">'+esc(wm.desc)+' &middot; feels '+esc(wm.feelsC)+'&#176;C &middot; &#128167;'+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()">&#9998; 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
- '<div class="card"><div class="card__title">Agents</div><div class="card__value">38</div><div class="card__sub">Ready</div></div>'+
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();