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 +1 -1
- package/src/commands/chat.mjs +56 -4
- package/src/commands/ui.mjs +12 -3
- package/src/commands/voice.mjs +12 -4
- package/src/constants.mjs +1 -1
- package/src/services/google-gmail.mjs +54 -0
- package/src/services/mail-router.mjs +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "6.
|
|
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": {
|
package/src/commands/chat.mjs
CHANGED
|
@@ -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.
|
|
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',
|
|
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) ──────────────────────────
|
package/src/commands/ui.mjs
CHANGED
|
@@ -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',
|
|
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 || '');
|
package/src/commands/voice.mjs
CHANGED
|
@@ -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',
|
|
246
|
-
|
|
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.
|
|
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
|
+
}
|