nothumanallowed 13.2.64 → 13.2.66
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 +36 -6
- package/src/constants.mjs +1 -1
- package/src/services/google-calendar.mjs +29 -16
- package/src/services/web-ui.mjs +47 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.2.
|
|
3
|
+
"version": "13.2.66",
|
|
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
|
@@ -1009,17 +1009,47 @@ export async function cmdUI(args) {
|
|
|
1009
1009
|
return;
|
|
1010
1010
|
}
|
|
1011
1011
|
|
|
1012
|
-
// GET /api/calendar?date=YYYY-MM-DD
|
|
1012
|
+
// GET /api/calendar?date=YYYY-MM-DD OR ?month=YYYY-MM (loads entire month)
|
|
1013
1013
|
if (method === 'GET' && pathname === '/api/calendar') {
|
|
1014
1014
|
try {
|
|
1015
1015
|
const dateParam = url.searchParams.get('date');
|
|
1016
|
-
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1016
|
+
const monthParam = url.searchParams.get('month'); // e.g. "2026-05"
|
|
1017
|
+
if (monthParam) {
|
|
1018
|
+
// Load entire month across all calendars in one shot
|
|
1019
|
+
const [y, m] = monthParam.split('-').map(Number);
|
|
1020
|
+
const startOfMonth = new Date(y, m - 1, 1);
|
|
1021
|
+
const endOfMonth = new Date(y, m, 1);
|
|
1022
|
+
const { listCalendars: lc, listEvents: le } = await import('../services/google-calendar.mjs');
|
|
1023
|
+
const calendars = await lc(config);
|
|
1024
|
+
const byDate = {};
|
|
1025
|
+
for (const cal of calendars) {
|
|
1026
|
+
if (cal.accessRole === 'freeBusyReader') continue;
|
|
1027
|
+
const isHolidayFeed = cal.id.includes('#holiday@group');
|
|
1028
|
+
try {
|
|
1029
|
+
const evts = await le(config, cal.id, startOfMonth, endOfMonth);
|
|
1030
|
+
for (const e of evts) {
|
|
1031
|
+
e.calendarId = cal.id;
|
|
1032
|
+
e.calendarName = cal.summary;
|
|
1033
|
+
e.readOnly = cal.accessRole === 'reader' || cal.accessRole === 'freeBusyReader';
|
|
1034
|
+
e._isHoliday = isHolidayFeed;
|
|
1035
|
+
const dk = (e.start || '').slice(0, 10);
|
|
1036
|
+
if (!byDate[dk]) byDate[dk] = [];
|
|
1037
|
+
// Dedup holidays per date
|
|
1038
|
+
if (isHolidayFeed && byDate[dk].some(x => x._isHoliday)) continue;
|
|
1039
|
+
byDate[dk].push(e);
|
|
1040
|
+
}
|
|
1041
|
+
} catch { /* skip */ }
|
|
1042
|
+
}
|
|
1043
|
+
sendJSON(res, 200, { byDate, month: monthParam });
|
|
1019
1044
|
} else {
|
|
1020
|
-
events
|
|
1045
|
+
let events;
|
|
1046
|
+
if (dateParam) {
|
|
1047
|
+
events = await getEventsForDate(config, dateParam);
|
|
1048
|
+
} else {
|
|
1049
|
+
events = await getTodayEvents(config);
|
|
1050
|
+
}
|
|
1051
|
+
sendJSON(res, 200, { events, date: dateParam || new Date().toISOString().split('T')[0] });
|
|
1021
1052
|
}
|
|
1022
|
-
sendJSON(res, 200, { events, date: dateParam || new Date().toISOString().split('T')[0] });
|
|
1023
1053
|
} catch (e) {
|
|
1024
1054
|
sendJSON(res, 200, { events: [], error: e.message });
|
|
1025
1055
|
}
|
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.2.
|
|
8
|
+
export const VERSION = '13.2.66';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -98,25 +98,27 @@ export async function getTodayEvents(config) {
|
|
|
98
98
|
const allEvents = [];
|
|
99
99
|
|
|
100
100
|
for (const cal of calendars) {
|
|
101
|
-
if (cal.accessRole === 'freeBusyReader') continue;
|
|
101
|
+
if (cal.accessRole === 'freeBusyReader') continue;
|
|
102
|
+
const isHolidayFeed = cal.id.includes('#holiday@group');
|
|
102
103
|
try {
|
|
103
104
|
const events = await listEvents(config, cal.id, startOfDay, endOfDay);
|
|
104
105
|
for (const e of events) {
|
|
105
106
|
e.calendarName = cal.summary;
|
|
106
107
|
e.calendarId = cal.id;
|
|
107
108
|
e.readOnly = cal.accessRole === 'reader' || cal.accessRole === 'freeBusyReader';
|
|
109
|
+
e._isHoliday = isHolidayFeed;
|
|
108
110
|
allEvents.push(e);
|
|
109
111
|
}
|
|
110
112
|
} catch { /* skip failed calendars */ }
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
// Sort by start time, then deduplicate same-day same-title events (holiday feeds)
|
|
114
115
|
allEvents.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
|
115
|
-
const
|
|
116
|
+
const holidayDates = new Set();
|
|
116
117
|
return allEvents.filter(e => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
if (!e._isHoliday) return true;
|
|
119
|
+
const dateKey = (e.start || '').slice(0, 10);
|
|
120
|
+
if (holidayDates.has(dateKey)) return false;
|
|
121
|
+
holidayDates.add(dateKey);
|
|
120
122
|
return true;
|
|
121
123
|
});
|
|
122
124
|
}
|
|
@@ -125,8 +127,15 @@ export async function getTodayEvents(config) {
|
|
|
125
127
|
* Get events for a specific date.
|
|
126
128
|
*/
|
|
127
129
|
export async function getEventsForDate(config, date) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
// Parse date string as LOCAL time to avoid UTC-midnight timezone shift
|
|
131
|
+
let startOfDay;
|
|
132
|
+
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
133
|
+
const [y, m, d] = date.split('-').map(Number);
|
|
134
|
+
startOfDay = new Date(y, m - 1, d);
|
|
135
|
+
} else {
|
|
136
|
+
const d = new Date(date);
|
|
137
|
+
startOfDay = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
138
|
+
}
|
|
130
139
|
const endOfDay = new Date(startOfDay.getTime() + 86400000);
|
|
131
140
|
|
|
132
141
|
// Load from all calendars so calendarId is always accurate
|
|
@@ -134,26 +143,30 @@ export async function getEventsForDate(config, date) {
|
|
|
134
143
|
const allEvents = [];
|
|
135
144
|
for (const cal of calendars) {
|
|
136
145
|
if (cal.accessRole === 'freeBusyReader') continue;
|
|
146
|
+
const isHolidayFeed = cal.id.includes('#holiday@group');
|
|
137
147
|
try {
|
|
138
148
|
const events = await listEvents(config, cal.id, startOfDay, endOfDay);
|
|
139
149
|
for (const e of events) {
|
|
140
150
|
e.calendarName = cal.summary;
|
|
141
151
|
e.calendarId = cal.id;
|
|
142
152
|
e.readOnly = cal.accessRole === 'reader' || cal.accessRole === 'freeBusyReader';
|
|
153
|
+
e._isHoliday = isHolidayFeed;
|
|
143
154
|
allEvents.push(e);
|
|
144
155
|
}
|
|
145
156
|
} catch { /* skip */ }
|
|
146
157
|
}
|
|
147
158
|
allEvents.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
|
148
159
|
|
|
149
|
-
// Deduplicate:
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
// Deduplicate: for holiday feeds, keep only ONE holiday per date
|
|
161
|
+
// (IT + EN feeds have same day, different language titles — keep first)
|
|
162
|
+
const holidayDates = new Set();
|
|
163
|
+
return allEvents.filter(e => {
|
|
164
|
+
if (!e._isHoliday) return true;
|
|
165
|
+
const dateKey = (e.start || '').slice(0, 10);
|
|
166
|
+
if (holidayDates.has(dateKey)) return false;
|
|
167
|
+
holidayDates.add(dateKey);
|
|
168
|
+
return true;
|
|
169
|
+
});
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
/**
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -420,8 +420,11 @@ function renderMessages(){
|
|
|
420
420
|
if(isA&&m.inlineHtml){
|
|
421
421
|
inlineBlock=m.inlineHtml.replace(/\\[INLINE_CARD\\]([\\s\\S]*?)\\[\\/INLINE_CARD\\]/g,function(_,htm){return '<div class="inline-card">'+htm+'</div>';}).replace(/\\[INLINE_BROWSER\\]([^|]+)\\|([^\\]]+)\\[\\/INLINE_BROWSER\\]/g,function(_,file,url){return '<div class="inline-browser"><div class="inline-browser-bar"><span class="inline-browser-dot"></span><span class="inline-browser-dot"></span><span class="inline-browser-dot"></span><span class="inline-browser-url">'+esc(url)+'</span></div><img src="/api/screenshots/'+esc(file)+'" alt="'+esc(url)+'" onclick="openLightbox(this.src)" style="cursor:zoom-in"></div>';});
|
|
422
422
|
}
|
|
423
|
+
var isStreaming=isA&&(m.content==='Thinking...'||m.content==='');
|
|
423
424
|
var bubbleCls=isA?'msg__bubble md-body':'msg__bubble';
|
|
424
|
-
|
|
425
|
+
var displayContent=isStreaming?'<div class="typing-dots"><span></span><span></span><span></span></div>':content;
|
|
426
|
+
var streamCls=isStreaming?' msg--streaming':'';
|
|
427
|
+
h+='<div class="msg msg--'+esc(m.role)+streamCls+'"><div class="msg__label">'+esc(m.role==='user'?'You':'NHA')+'</div><div class="'+bubbleCls+'">'+displayContent+'</div>'+inlineBlock+acts+'</div>';
|
|
425
428
|
});
|
|
426
429
|
el.innerHTML=h;el.scrollTop=el.scrollHeight;
|
|
427
430
|
// Load fork info for messages that have IDs
|
|
@@ -882,7 +885,7 @@ function sendChat(){
|
|
|
882
885
|
displayContent=displayContent.replace(/<think>[\\s\\S]*?<\\/think>/g,'').trim();
|
|
883
886
|
}
|
|
884
887
|
var el=document.getElementById('chatMessages');
|
|
885
|
-
if(el){var msgs=el.querySelectorAll('.msg');var last=msgs[msgs.length-1];if(last){var bub=last.querySelector('.msg__bubble');if(bub){bub.className='msg__bubble md-body';
|
|
888
|
+
if(el){var msgs=el.querySelectorAll('.msg');var last=msgs[msgs.length-1];if(last){last.classList.add('msg--streaming');var bub=last.querySelector('.msg__bubble');if(bub){bub.className='msg__bubble md-body';var renderedContent=isThinking?displayContent:renderMd(displayContent);bub.innerHTML=renderedContent||'<div class="typing-dots"><span></span><span></span><span></span></div>';}}el.scrollTop=el.scrollHeight;}
|
|
886
889
|
}
|
|
887
890
|
if(currentEvent==='tool'){
|
|
888
891
|
var toolLabels={browser_open:'Opening page',browser_screenshot:'Taking screenshot',browser_click:'Clicking element',browser_type:'Typing text',browser_extract:'Extracting content',browser_js:'Running JavaScript',browser_wait:'Waiting for element',browser_scroll:'Scrolling page',browser_key:'Pressing key',browser_close:'Closing browser',web_search:'Searching the web',fetch_url:'Fetching URL',gmail_list:'Searching emails',gmail_read:'Reading email',gmail_send:'Sending email',calendar_today:'Loading calendar',calendar_create:'Creating event'};
|
|
@@ -1115,29 +1118,38 @@ function renderCalendar(el){
|
|
|
1115
1118
|
|
|
1116
1119
|
// Day headers
|
|
1117
1120
|
h+='<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:2px;margin-bottom:4px">';
|
|
1118
|
-
['Mon','Tue','Wed','Thu','Fri','Sat','Sun'].forEach(function(d){
|
|
1119
|
-
|
|
1121
|
+
['Mon','Tue','Wed','Thu','Fri','Sat','Sun'].forEach(function(d,i){
|
|
1122
|
+
var isWe=i>=5;
|
|
1123
|
+
h+='<div style="text-align:center;font-size:10px;color:'+(isWe?'var(--red)':'var(--dim)')+';padding:4px;font-weight:'+(isWe?'600':'400')+'">'+d+'</div>';
|
|
1120
1124
|
});
|
|
1121
1125
|
h+='</div>';
|
|
1122
1126
|
|
|
1123
1127
|
// Calendar grid - square cells
|
|
1124
1128
|
h+='<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:3px">';
|
|
1125
1129
|
// Empty cells before first day
|
|
1126
|
-
for(var i=0;i<startDay;i++){
|
|
1130
|
+
for(var i=0;i<startDay;i++){
|
|
1131
|
+
var isWeCol=i>=5;
|
|
1132
|
+
h+='<div style="aspect-ratio:1;background:'+(isWeCol?'rgba(255,80,80,0.04)':'var(--bg)')+';border-radius:6px"></div>';
|
|
1133
|
+
}
|
|
1127
1134
|
// Day cells
|
|
1128
1135
|
for(var d=1;d<=daysInMonth;d++){
|
|
1129
1136
|
var key=calKey(calYear,calMonth,d);
|
|
1130
1137
|
var today=isToday(calYear,calMonth,d);
|
|
1131
1138
|
var evts=calEventsCache[key]||[];
|
|
1132
1139
|
var count=evts.length;
|
|
1133
|
-
var
|
|
1134
|
-
var
|
|
1140
|
+
var dayOfWeek=(startDay+d-1)%7; // 0=Mon … 5=Sat, 6=Sun
|
|
1141
|
+
var isWeekend=dayOfWeek>=5;
|
|
1142
|
+
var hasHoliday=evts.some(function(e){return e._isHoliday||e.readOnly});
|
|
1143
|
+
var bg=today?'var(--greendim)':isWeekend?'rgba(255,80,80,0.06)':'var(--bg2)';
|
|
1144
|
+
var bdr=today?'var(--green3)':hasHoliday?'var(--red)':count>0?'var(--amber)':'var(--border)';
|
|
1145
|
+
var numColor=today?'var(--green)':isWeekend?'var(--red)':'var(--text)';
|
|
1135
1146
|
h+='<div onclick="openDayDetail(\\x27'+key+'\\x27)" style="aspect-ratio:1;background:'+bg+';border:1px solid '+bdr+';border-radius:6px;padding:6px;cursor:pointer;display:flex;flex-direction:column;overflow:hidden">';
|
|
1136
|
-
h+='<div style="font-size:14px;font-weight:'+(today?'800':'500')+';color:'+
|
|
1147
|
+
h+='<div style="font-size:14px;font-weight:'+(today?'800':'500')+';color:'+numColor+'">'+d+'</div>';
|
|
1137
1148
|
if(count>0){
|
|
1138
1149
|
h+='<div style="flex:1;display:flex;flex-direction:column;justify-content:flex-end;gap:1px;min-height:0">';
|
|
1139
1150
|
evts.slice(0,2).forEach(function(x){
|
|
1140
|
-
|
|
1151
|
+
var evtColor=x._isHoliday||x.readOnly?'var(--red)':'var(--amber)';
|
|
1152
|
+
h+='<div style="font-size:8px;color:'+evtColor+';overflow:hidden;white-space:nowrap;text-overflow:ellipsis;background:var(--bg3);border-radius:2px;padding:1px 3px">'+esc(x.summary)+'</div>';
|
|
1141
1153
|
});
|
|
1142
1154
|
if(count>2)h+='<div style="font-size:8px;color:var(--dim);text-align:center">+'+String(count-2)+'</div>';
|
|
1143
1155
|
h+='</div>';
|
|
@@ -1154,26 +1166,25 @@ function renderCalendar(el){
|
|
|
1154
1166
|
}
|
|
1155
1167
|
|
|
1156
1168
|
function loadMonthEvents(){
|
|
1169
|
+
var monthKey=calYear+'-'+String(calMonth+1).padStart(2,'0');
|
|
1170
|
+
// Check if already loaded
|
|
1157
1171
|
var daysInMonth=new Date(calYear,calMonth+1,0).getDate();
|
|
1158
|
-
var
|
|
1159
|
-
for(var d=1;d<=daysInMonth;d++){
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1172
|
+
var allLoaded=true;
|
|
1173
|
+
for(var d=1;d<=daysInMonth;d++){if(!calEventsCache[calKey(calYear,calMonth,d)]){allLoaded=false;break}}
|
|
1174
|
+
if(allLoaded){var li=document.getElementById('calLoading');if(li)li.style.display='none';return;}
|
|
1175
|
+
|
|
1176
|
+
apiGet('/api/calendar?month='+monthKey).then(function(r){
|
|
1177
|
+
if(r&&r.byDate){
|
|
1178
|
+
// Fill all days — ensure days with no events get empty array so we don't re-fetch
|
|
1179
|
+
for(var d=1;d<=daysInMonth;d++){
|
|
1180
|
+
var k=calKey(calYear,calMonth,d);
|
|
1181
|
+
calEventsCache[k]=r.byDate[k]||[];
|
|
1182
|
+
}
|
|
1167
1183
|
}
|
|
1168
|
-
}
|
|
1169
|
-
if(promises.length===0){
|
|
1170
1184
|
var li=document.getElementById('calLoading');if(li)li.style.display='none';
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
|
-
Promise.all(promises).then(function(){
|
|
1174
|
-
var li=document.getElementById('calLoading');if(li)li.style.display='none';
|
|
1175
|
-
// Re-render just the grid cells with events
|
|
1176
1185
|
renderCalendar(document.getElementById('content'));
|
|
1186
|
+
}).catch(function(){
|
|
1187
|
+
var li=document.getElementById('calLoading');if(li)li.style.display='none';
|
|
1177
1188
|
});
|
|
1178
1189
|
}
|
|
1179
1190
|
|
|
@@ -4136,6 +4147,10 @@ function init(){
|
|
|
4136
4147
|
if(bv)makeDraggable(bv,\x27.browser-viewer__header\x27);
|
|
4137
4148
|
var cp=document.getElementById('canvasPanel');
|
|
4138
4149
|
if(cp)makeDraggable(cp,\x27.cvs-header\x27);
|
|
4150
|
+
// Telemetry ping — fire and forget
|
|
4151
|
+
setTimeout(function(){
|
|
4152
|
+
fetch(\x27https://nothumanallowed.com/api/v1/telemetry/ping\x27,{method:\x27POST\x27,headers:{\x27Content-Type\x27:\x27application/json\x27},body:JSON.stringify({platform:\x27web-ui\x27,version:VERSION})}).catch(function(){});
|
|
4153
|
+
},3000);
|
|
4139
4154
|
}
|
|
4140
4155
|
init();
|
|
4141
4156
|
`;
|
|
@@ -4240,9 +4255,15 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
4240
4255
|
.chat__empty-hint{font-size:11px;margin-top:12px}
|
|
4241
4256
|
.msg{margin-bottom:12px}
|
|
4242
4257
|
.msg--user .msg__bubble{background:var(--bg3);border:1px solid var(--border2);border-radius:8px 8px 2px 8px;padding:10px 14px;max-width:85%;margin-left:auto;color:var(--bright)}
|
|
4243
|
-
.msg--assistant .msg__bubble{background:var(--greendim);border:1px solid var(--green3);border-radius:8px 8px 8px 2px;padding:10px 14px;max-width:85%;color:var(--text);white-space:pre-wrap;word-wrap:break-word;line-height:1.5}
|
|
4258
|
+
.msg--assistant .msg__bubble{background:var(--greendim);border:1px solid var(--green3);border-radius:8px 8px 8px 2px;padding:10px 14px;max-width:85%;color:var(--text);white-space:pre-wrap;word-wrap:break-word;line-height:1.5;min-height:40px;min-width:60px}
|
|
4244
4259
|
.msg--assistant .msg__bubble img{max-width:100%;border-radius:8px;margin:8px 0;border:1px solid rgba(0,255,65,0.2)}
|
|
4260
|
+
.msg--assistant.msg--streaming .msg__bubble{border-color:var(--green);box-shadow:0 0 8px rgba(0,255,65,0.15)}
|
|
4245
4261
|
.msg__label{font-size:10px;color:var(--dim);margin-bottom:2px}
|
|
4262
|
+
.typing-dots{display:inline-flex;align-items:center;gap:4px;padding:4px 0}
|
|
4263
|
+
.typing-dots span{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--green);opacity:0.3;animation:tdot 1.2s ease-in-out infinite}
|
|
4264
|
+
.typing-dots span:nth-child(2){animation-delay:0.2s}
|
|
4265
|
+
.typing-dots span:nth-child(3){animation-delay:0.4s}
|
|
4266
|
+
@keyframes tdot{0%,80%,100%{opacity:0.2;transform:scale(0.8)}40%{opacity:1;transform:scale(1.2)}}
|
|
4246
4267
|
.msg__actions{display:flex;gap:6px;margin-top:4px;opacity:0.4;transition:opacity 0.2s}
|
|
4247
4268
|
.msg:hover .msg__actions{opacity:1}
|
|
4248
4269
|
.msg__actions button{background:none;border:none;color:var(--dim);cursor:pointer;font-size:10px;font-family:var(--mono);padding:2px 4px}
|