nothumanallowed 6.5.2 → 6.6.0

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": "6.5.2",
3
+ "version": "6.6.0",
4
4
  "description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Per-agent memory, Telegram + Discord auto-responder, proactive intelligence daemon, voice chat, plugin system.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -133,7 +133,16 @@ TOOLS:
133
133
  Update ANY field of an existing calendar event: title, location, description, start time, end time.
134
134
  You MUST call calendar_find first to get the eventId. Only include fields that need to change. ALWAYS confirm before updating.
135
135
 
136
- 21. maps_directions(from: string, to: string)
136
+ 21. gmail_mark_read(messageId?: string, all?: boolean)
137
+ Mark email(s) as read. If all=true, marks ALL unread emails as read. If messageId provided, marks that single email.
138
+
139
+ 22. gmail_mark_unread(messageId: string)
140
+ Mark a specific email as unread.
141
+
142
+ 23. gmail_archive(messageId: string)
143
+ Archive a specific email (removes from inbox).
144
+
145
+ 24. maps_directions(from: string, to: string)
137
146
  Generate a Google Maps directions link between two locations. Returns a clickable URL.
138
147
  Use this when the user asks for directions, route, or "how to get to" somewhere.
139
148
 
@@ -389,7 +398,23 @@ async function executeTool(action, params, config) {
389
398
 
390
399
  // ── Calendar Update (modify any field of existing event) ──────────────
391
400
  case 'calendar_update': {
392
- const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
401
+ const { updateEvent: updateCal, listEvents: listEvR } = await import('../services/mail-router.mjs');
402
+
403
+ // Smart eventId resolution: if it looks like a name instead of a Google Calendar ID, search for it
404
+ let eventId = params.eventId;
405
+ if (eventId && (eventId.includes(' ') || eventId.length < 10 || /[A-Z]/.test(eventId))) {
406
+ // Probably a name, not an ID — search for it
407
+ const from = new Date();
408
+ const to = new Date(from.getTime() + 14 * 86400000);
409
+ const events = await listEvR(config, 'primary', from, to);
410
+ const match = events.find(e => (e.summary || '').toLowerCase().includes(eventId.toLowerCase()));
411
+ if (match) {
412
+ eventId = match.id;
413
+ } else {
414
+ return `Could not find event matching "${params.eventId}" in the next 2 weeks. Use calendar_find to search.`;
415
+ }
416
+ }
417
+
393
418
  const patch = {};
394
419
  if (params.summary) patch.summary = params.summary;
395
420
  if (params.location) patch.location = params.location;
@@ -402,9 +427,36 @@ async function executeTool(action, params, config) {
402
427
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
403
428
  patch.end = { dateTime: new Date(params.end).toISOString(), timeZone: tz };
404
429
  }
405
- await updateCal(config, 'primary', params.eventId, patch);
430
+ await updateCal(config, 'primary', eventId, patch);
406
431
  const changes = Object.keys(patch).join(', ');
407
- return `Event updated (${changes}). ${params.location ? `New location: ${params.location}` : ''}`;
432
+ return `Event updated successfully (${changes}). ${params.location ? `New location: ${params.location}` : ''}`;
433
+ }
434
+
435
+ // ── Gmail Mark Read/Unread/Archive ──────────────────────────────────
436
+ case 'gmail_mark_read': {
437
+ if (params.all) {
438
+ const { markAllAsRead } = await import('../services/mail-router.mjs');
439
+ const result = await markAllAsRead(config);
440
+ return `Done! ${result.count} email${result.count !== 1 ? 's' : ''} marked as read.`;
441
+ }
442
+ if (params.messageId) {
443
+ const { markAsRead } = await import('../services/mail-router.mjs');
444
+ await markAsRead(config, params.messageId);
445
+ return `Email ${params.messageId} marked as read.`;
446
+ }
447
+ return 'Specify a messageId or set all=true to mark all as read.';
448
+ }
449
+
450
+ case 'gmail_mark_unread': {
451
+ const { markAsUnread } = await import('../services/mail-router.mjs');
452
+ await markAsUnread(config, params.messageId);
453
+ return `Email ${params.messageId} marked as unread.`;
454
+ }
455
+
456
+ case 'gmail_archive': {
457
+ const { archiveMessage } = await import('../services/mail-router.mjs');
458
+ await archiveMessage(config, params.messageId);
459
+ return `Email ${params.messageId} archived.`;
408
460
  }
409
461
 
410
462
  // ── Maps Directions (free Google Maps link) ──────────────────────────
@@ -321,7 +321,16 @@ async function executeTool(action, params, config) {
321
321
  }).join('\n');
322
322
  }
323
323
  case 'calendar_update': {
324
- const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
324
+ const { updateEvent: updateCal, listEvents: listEvAuto } = await import('../services/mail-router.mjs');
325
+ let eventId = params.eventId;
326
+ if (eventId && (eventId.includes(' ') || eventId.length < 10 || /[A-Z]/.test(eventId))) {
327
+ const from = new Date();
328
+ const to = new Date(from.getTime() + 14 * 86400000);
329
+ const events = await listEvAuto(config, 'primary', from, to);
330
+ const match = events.find(e => (e.summary || '').toLowerCase().includes(eventId.toLowerCase()));
331
+ if (match) eventId = match.id;
332
+ else return `Could not find event matching "${params.eventId}".`;
333
+ }
325
334
  const patch = {};
326
335
  if (params.summary) patch.summary = params.summary;
327
336
  if (params.location) patch.location = params.location;
@@ -334,9 +343,9 @@ async function executeTool(action, params, config) {
334
343
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
335
344
  patch.end = { dateTime: new Date(params.end).toISOString(), timeZone: tz };
336
345
  }
337
- await updateCal(config, 'primary', params.eventId, patch);
346
+ await updateCal(config, 'primary', eventId, patch);
338
347
  const changes = Object.keys(patch).join(', ');
339
- return `Event updated (${changes}). ${params.location ? 'New location: ' + params.location : ''}`;
348
+ return `Event updated successfully (${changes}). ${params.location ? 'New location: ' + params.location : ''}`;
340
349
  }
341
350
  case 'maps_directions': {
342
351
  const from = encodeURIComponent(params.from || '');
@@ -229,7 +229,16 @@ async function executeTool(action, params, config) {
229
229
  return matches.map((e, i) => `${i + 1}. Event ID ${e.id}, ${e.start.split('T')[0]}, ${e.summary}`).join('. ');
230
230
  }
231
231
  case 'calendar_update': {
232
- const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
232
+ const { updateEvent: updateCal, listEvents: listEvAuto } = await import('../services/mail-router.mjs');
233
+ let eventId = params.eventId;
234
+ if (eventId && (eventId.includes(' ') || eventId.length < 10 || /[A-Z]/.test(eventId))) {
235
+ const from = new Date();
236
+ const to = new Date(from.getTime() + 14 * 86400000);
237
+ const events = await listEvAuto(config, 'primary', from, to);
238
+ const match = events.find(e => (e.summary || '').toLowerCase().includes(eventId.toLowerCase()));
239
+ if (match) eventId = match.id;
240
+ else return 'Could not find that event.';
241
+ }
233
242
  const patch = {};
234
243
  if (params.summary) patch.summary = params.summary;
235
244
  if (params.location) patch.location = params.location;
@@ -242,9 +251,8 @@ async function executeTool(action, params, config) {
242
251
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
243
252
  patch.end = { dateTime: new Date(params.end).toISOString(), timeZone: tz };
244
253
  }
245
- await updateCal(config, 'primary', params.eventId, patch);
246
- const changes = Object.keys(patch).join(', ');
247
- return `Event updated (${changes}). ${params.location ? 'New location: ' + params.location : ''}`;
254
+ await updateCal(config, 'primary', eventId, patch);
255
+ return 'Event updated. ' + (params.location ? 'Location set to ' + params.location : '');
248
256
  }
249
257
  case 'maps_directions': {
250
258
  const from = encodeURIComponent(params.from || '');
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 = '6.5.2';
8
+ export const VERSION = '6.6.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -178,6 +178,60 @@ export async function listLabels(config) {
178
178
  return data.labels || [];
179
179
  }
180
180
 
181
+ /**
182
+ * Modify labels on a message (add/remove).
183
+ */
184
+ export async function modifyMessage(config, messageId, addLabels = [], removeLabels = []) {
185
+ return gmailFetch(config, `/messages/${messageId}/modify`, {
186
+ method: 'POST',
187
+ headers: { 'Content-Type': 'application/json' },
188
+ body: JSON.stringify({ addLabelIds: addLabels, removeLabelIds: removeLabels }),
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Mark a single message as read.
194
+ */
195
+ export async function markAsRead(config, messageId) {
196
+ return modifyMessage(config, messageId, [], ['UNREAD']);
197
+ }
198
+
199
+ /**
200
+ * Mark a single message as unread.
201
+ */
202
+ export async function markAsUnread(config, messageId) {
203
+ return modifyMessage(config, messageId, ['UNREAD'], []);
204
+ }
205
+
206
+ /**
207
+ * Archive a message (remove INBOX label).
208
+ */
209
+ export async function archiveMessage(config, messageId) {
210
+ return modifyMessage(config, messageId, [], ['INBOX']);
211
+ }
212
+
213
+ /**
214
+ * Batch modify multiple messages (mark all as read, archive all, etc.)
215
+ */
216
+ export async function batchModify(config, messageIds, addLabels = [], removeLabels = []) {
217
+ return gmailFetch(config, '/messages/batchModify', {
218
+ method: 'POST',
219
+ headers: { 'Content-Type': 'application/json' },
220
+ body: JSON.stringify({ ids: messageIds, addLabelIds: addLabels, removeLabelIds: removeLabels }),
221
+ });
222
+ }
223
+
224
+ /**
225
+ * Mark ALL unread messages as read.
226
+ */
227
+ export async function markAllAsRead(config) {
228
+ const unread = await listMessages(config, 'is:unread', 500);
229
+ if (unread.length === 0) return { count: 0 };
230
+ const ids = unread.map(m => m.id);
231
+ await batchModify(config, ids, [], ['UNREAD']);
232
+ return { count: ids.length };
233
+ }
234
+
181
235
  // ── Message Parser ─────────────────────────────────────────────────────────
182
236
 
183
237
  function parseMessage(raw) {
@@ -296,3 +296,43 @@ export async function listEvents(config, calendarId = 'primary', timeMin, timeMa
296
296
  const gc = await getGoogleCalendar();
297
297
  return gc.listEvents(config, calendarId, timeMin, timeMax);
298
298
  }
299
+
300
+ /**
301
+ * Mark a message as read.
302
+ */
303
+ export async function markAsRead(config, messageId) {
304
+ const provider = detectMailProvider(config);
305
+ if (!provider) throw new Error('No mail provider authenticated.');
306
+ const gm = await getGoogleMail();
307
+ return gm.markAsRead(config, messageId);
308
+ }
309
+
310
+ /**
311
+ * Mark a message as unread.
312
+ */
313
+ export async function markAsUnread(config, messageId) {
314
+ const provider = detectMailProvider(config);
315
+ if (!provider) throw new Error('No mail provider authenticated.');
316
+ const gm = await getGoogleMail();
317
+ return gm.markAsUnread(config, messageId);
318
+ }
319
+
320
+ /**
321
+ * Archive a message.
322
+ */
323
+ export async function archiveMessage(config, messageId) {
324
+ const provider = detectMailProvider(config);
325
+ if (!provider) throw new Error('No mail provider authenticated.');
326
+ const gm = await getGoogleMail();
327
+ return gm.archiveMessage(config, messageId);
328
+ }
329
+
330
+ /**
331
+ * Mark ALL unread messages as read.
332
+ */
333
+ export async function markAllAsRead(config) {
334
+ const provider = detectMailProvider(config);
335
+ if (!provider) throw new Error('No mail provider authenticated.');
336
+ const gm = await getGoogleMail();
337
+ return gm.markAllAsRead(config);
338
+ }