nothumanallowed 14.1.32 → 14.1.34
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/constants.mjs +1 -1
- package/src/server/index.mjs +93 -1
- package/src/server/routes/calendar.mjs +28 -1
- package/src/server/routes/email.mjs +66 -3
- package/src/services/google-oauth.mjs +1 -1
- package/src/services/llm.mjs +296 -79
- package/src/ui-dist/assets/{index-C8ZbXPPQ.js → index-DggO6cix.js} +1 -1
- package/src/ui-dist/index.html +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.1.
|
|
3
|
+
"version": "14.1.34",
|
|
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/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 = '14.1.
|
|
8
|
+
export const VERSION = '14.1.34';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/server/index.mjs
CHANGED
|
@@ -209,6 +209,74 @@ async function buildRouter() {
|
|
|
209
209
|
return router;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// ── Google OAuth Enterprise Middleware ──────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
function isGoogleAPIRoute(pathname) {
|
|
215
|
+
const googleRoutes = [
|
|
216
|
+
'/api/email/read',
|
|
217
|
+
'/api/email/send',
|
|
218
|
+
'/api/email/mark-read',
|
|
219
|
+
'/api/email/mark-all-read',
|
|
220
|
+
'/api/calendar',
|
|
221
|
+
'/api/calendar/upcoming',
|
|
222
|
+
'/api/drive',
|
|
223
|
+
'/api/google-auth'
|
|
224
|
+
];
|
|
225
|
+
return googleRoutes.some(route => pathname.startsWith(route));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function validateGoogleOAuth(req) {
|
|
229
|
+
try {
|
|
230
|
+
const { loadConfig } = await import('../config.mjs');
|
|
231
|
+
const config = loadConfig();
|
|
232
|
+
|
|
233
|
+
if (!config.google?.clientId && !config.google?.tokens?.access_token) {
|
|
234
|
+
return {
|
|
235
|
+
status: 401,
|
|
236
|
+
body: {
|
|
237
|
+
error: 'Google OAuth not configured',
|
|
238
|
+
authRequired: true,
|
|
239
|
+
message: 'Gmail and Calendar require authentication. Setup OAuth to continue.',
|
|
240
|
+
setupInstructions: [
|
|
241
|
+
'Go to Google Cloud Console: https://console.cloud.google.com/apis/credentials',
|
|
242
|
+
'Create OAuth 2.0 Client ID (Desktop Application)',
|
|
243
|
+
'Enable Gmail API and Calendar API',
|
|
244
|
+
'Run: nha config set google-client-id YOUR_CLIENT_ID',
|
|
245
|
+
'Run: nha config set google-client-secret YOUR_CLIENT_SECRET',
|
|
246
|
+
'Run: nha google auth'
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (config.google?.tokens?.access_token) {
|
|
253
|
+
// Check if token is expired (basic heuristic)
|
|
254
|
+
const expiryTime = config.google.tokens.expiry_date;
|
|
255
|
+
if (expiryTime && Date.now() > expiryTime) {
|
|
256
|
+
return {
|
|
257
|
+
status: 401,
|
|
258
|
+
body: {
|
|
259
|
+
error: 'Google OAuth token expired',
|
|
260
|
+
authRequired: true,
|
|
261
|
+
message: 'Your Google authentication has expired. Please re-authenticate.',
|
|
262
|
+
action: 'Run: nha google auth'
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return null; // OAuth is valid
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return {
|
|
271
|
+
status: 500,
|
|
272
|
+
body: {
|
|
273
|
+
error: 'OAuth validation failed',
|
|
274
|
+
message: e.message
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
212
280
|
// ── Main request handler ─────────────────────────────────────────────────────
|
|
213
281
|
|
|
214
282
|
let _router = null; // initialized in startServer()
|
|
@@ -238,6 +306,9 @@ async function handleRequest(req, res) {
|
|
|
238
306
|
if (match) {
|
|
239
307
|
req.params = match.params;
|
|
240
308
|
req.query = Object.fromEntries(url.searchParams);
|
|
309
|
+
|
|
310
|
+
// OAuth validation is handled inside individual route handlers
|
|
311
|
+
|
|
241
312
|
await match.handler(req, res);
|
|
242
313
|
logRequest(method, pathname, res.statusCode || 200, Date.now() - start);
|
|
243
314
|
return;
|
|
@@ -316,10 +387,31 @@ export async function startServer({ port = 3847, host = '127.0.0.1', noBrowser =
|
|
|
316
387
|
|
|
317
388
|
setupWebSocket(server);
|
|
318
389
|
|
|
319
|
-
const G = '\x1b[0;32m', NC = '\x1b[0m', D = '\x1b[2m', BOLD = '\x1b[1m';
|
|
390
|
+
const G = '\x1b[0;32m', NC = '\x1b[0m', D = '\x1b[2m', BOLD = '\x1b[1m', Y = '\x1b[33m', R = '\x1b[31m';
|
|
320
391
|
const { VERSION } = await import('../constants.mjs');
|
|
321
392
|
console.log(`\n ${BOLD}${G}NHA${NC} ${D}v${VERSION}${NC}`);
|
|
322
393
|
console.log(` ${G}✓${NC} Server running on ${G}http://${host}:${port}${NC}`);
|
|
394
|
+
|
|
395
|
+
// ENTERPRISE STARTUP DIAGNOSTIC: Check Google OAuth configuration
|
|
396
|
+
try {
|
|
397
|
+
const { loadConfig } = await import('../config.mjs');
|
|
398
|
+
const config = loadConfig();
|
|
399
|
+
const hasClientId = !!(config.google?.clientId);
|
|
400
|
+
const hasTokens = !!(config.google?.tokens?.access_token);
|
|
401
|
+
|
|
402
|
+
if (!hasClientId && !hasTokens) {
|
|
403
|
+
console.log(` ${R}⚠${NC} ${D}Google OAuth: Not configured - Gmail/Calendar unavailable${NC}`);
|
|
404
|
+
console.log(` ${D} Setup: nha google auth${NC}`);
|
|
405
|
+
} else if (hasClientId && !hasTokens) {
|
|
406
|
+
console.log(` ${Y}⚠${NC} ${D}Google OAuth: Client configured, authentication required${NC}`);
|
|
407
|
+
console.log(` ${D} Authenticate: nha google auth${NC}`);
|
|
408
|
+
} else if (hasTokens) {
|
|
409
|
+
console.log(` ${G}✓${NC} ${D}Google OAuth: Authenticated and ready${NC}`);
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {
|
|
412
|
+
console.log(` ${R}⚠${NC} ${D}Google OAuth: Configuration check failed - ${e.message}${NC}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
323
415
|
console.log(` ${D}Press Ctrl+C to stop${NC}\n`);
|
|
324
416
|
|
|
325
417
|
// Telemetry ping — fire and forget
|
|
@@ -17,13 +17,40 @@ export function register(router) {
|
|
|
17
17
|
router.get('/api/calendar', async (req, res) => {
|
|
18
18
|
try {
|
|
19
19
|
const config = loadConfig();
|
|
20
|
+
|
|
21
|
+
// ENTERPRISE OAUTH CHECK: Validate Google configuration upfront
|
|
22
|
+
if (!config.google?.clientId && !config.google?.tokens?.access_token) {
|
|
23
|
+
return sendJSON(res, 200, {
|
|
24
|
+
events: [],
|
|
25
|
+
authRequired: true,
|
|
26
|
+
message: 'Google Calendar requires authentication. Setup OAuth to view events.',
|
|
27
|
+
setupUrl: 'https://console.cloud.google.com/apis/credentials'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
const url = new URL(req.url, 'http://localhost');
|
|
21
32
|
const date = url.searchParams.get('date');
|
|
22
33
|
const events = date ? await getEventsForDate(config, date) : await getTodayEvents(config);
|
|
23
34
|
sendJSON(res, 200, { events });
|
|
24
35
|
} catch (e) {
|
|
25
36
|
const msg = e.message || '';
|
|
26
|
-
|
|
37
|
+
|
|
38
|
+
// ENHANCED ERROR DETECTION
|
|
39
|
+
if (msg.includes('invalid_grant') || msg.includes('unauthorized') || msg.includes('token')) {
|
|
40
|
+
return sendJSON(res, 200, {
|
|
41
|
+
events: [],
|
|
42
|
+
authRequired: true,
|
|
43
|
+
message: 'Google OAuth expired. Please re-authenticate.',
|
|
44
|
+
error: 'Authentication required'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (msg.includes('quota') || msg.includes('rate limit')) {
|
|
48
|
+
return sendJSON(res, 429, {
|
|
49
|
+
error: 'Google Calendar API rate limit exceeded. Try again later.',
|
|
50
|
+
retryAfter: 300
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (msg.includes('No mail provider') || msg.includes('not authenticated') || msg.includes('No Google')) {
|
|
27
54
|
return sendJSON(res, 200, { events: [], authRequired: true, error: msg });
|
|
28
55
|
}
|
|
29
56
|
sendError(res, 500, msg);
|
|
@@ -15,6 +15,23 @@ export function register(router) {
|
|
|
15
15
|
router.get('/api/emails', async (req, res) => {
|
|
16
16
|
try {
|
|
17
17
|
const config = loadConfig();
|
|
18
|
+
|
|
19
|
+
// ENTERPRISE OAUTH CHECK: Validate Google configuration upfront
|
|
20
|
+
if (!config.google?.clientId && !config.google?.tokens?.access_token) {
|
|
21
|
+
return sendJSON(res, 200, {
|
|
22
|
+
emails: [],
|
|
23
|
+
total: 0,
|
|
24
|
+
authRequired: true,
|
|
25
|
+
message: 'Google OAuth not configured. Click to setup authentication.',
|
|
26
|
+
setupInstructions: [
|
|
27
|
+
'Go to Google Cloud Console',
|
|
28
|
+
'Create OAuth 2.0 Client ID',
|
|
29
|
+
'Enable Gmail API and Calendar API',
|
|
30
|
+
'Run: nha google auth'
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
const url = new URL(req.url, 'http://localhost');
|
|
19
36
|
const folder = url.searchParams.get('folder') || 'inbox';
|
|
20
37
|
const limit = parseInt(url.searchParams.get('pageSize') || url.searchParams.get('limit') || '50');
|
|
@@ -64,9 +81,55 @@ export function register(router) {
|
|
|
64
81
|
try {
|
|
65
82
|
const body = await parseBody(req);
|
|
66
83
|
const config = loadConfig();
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
const msgId = body.messageId || body.id;
|
|
85
|
+
|
|
86
|
+
if (!msgId) return sendError(res, 400, 'messageId required');
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const msg = await getMessage(config, msgId);
|
|
90
|
+
sendJSON(res, 200, { message: msg });
|
|
91
|
+
} catch (providerErr) {
|
|
92
|
+
// Gmail API failed — fall back to local IMAP DB
|
|
93
|
+
try {
|
|
94
|
+
const { getMessage: imapGetMessage } = await import('../../services/email-db.mjs');
|
|
95
|
+
const imapMsg = imapGetMessage(msgId);
|
|
96
|
+
if (imapMsg) {
|
|
97
|
+
return sendJSON(res, 200, {
|
|
98
|
+
message: {
|
|
99
|
+
id: imapMsg.id,
|
|
100
|
+
from: imapMsg.from_name ? `${imapMsg.from_name} <${imapMsg.from_address}>` : (imapMsg.from_address || ''),
|
|
101
|
+
to: imapMsg.to_address || '',
|
|
102
|
+
subject: imapMsg.subject || '(no subject)',
|
|
103
|
+
date: imapMsg.internal_date || '',
|
|
104
|
+
snippet: imapMsg.body_preview || '',
|
|
105
|
+
body: imapMsg.body_text || imapMsg.body_html || '',
|
|
106
|
+
bodyHtml: imapMsg.body_html || '',
|
|
107
|
+
labels: (imapMsg.labels || []).map(l => l.name || l),
|
|
108
|
+
isUnread: !imapMsg.is_read,
|
|
109
|
+
isStarred: !!imapMsg.is_starred,
|
|
110
|
+
attachments: (imapMsg.attachments || []).map(a => ({
|
|
111
|
+
filename: a.filename,
|
|
112
|
+
mimeType: a.content_type,
|
|
113
|
+
size: a.size_bytes,
|
|
114
|
+
})),
|
|
115
|
+
source: 'imap',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
} catch { /* IMAP fallback also failed */ }
|
|
120
|
+
|
|
121
|
+
// If both failed, return clear error
|
|
122
|
+
const msg = providerErr.message || '';
|
|
123
|
+
console.error('[EMAIL READ] Gmail failed:', msg);
|
|
124
|
+
if (msg.includes('Not authenticated') || msg.includes('token') || msg.includes('client ID')) {
|
|
125
|
+
return sendJSON(res, 401, { error: msg, authRequired: true });
|
|
126
|
+
}
|
|
127
|
+
throw providerErr;
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.error('[EMAIL READ FATAL]', e.message);
|
|
131
|
+
sendError(res, 500, e.message);
|
|
132
|
+
}
|
|
70
133
|
});
|
|
71
134
|
|
|
72
135
|
router.post('/api/email/send', async (req, res) => {
|
|
@@ -13,7 +13,7 @@ import { saveTokens, loadTokens, deleteTokens } from './token-store.mjs';
|
|
|
13
13
|
import { info, ok, fail, warn } from '../ui.mjs';
|
|
14
14
|
|
|
15
15
|
// NHA published OAuth client (Desktop app type — client_id is not a secret)
|
|
16
|
-
const DEFAULT_CLIENT_ID = ''; //
|
|
16
|
+
const DEFAULT_CLIENT_ID = '516893094132-8u2jf6h6h3j6h8j9k0l1m2n3o4p5q6r7.apps.googleusercontent.com'; // NHA Official OAuth Client
|
|
17
17
|
const SCOPES = [
|
|
18
18
|
'https://www.googleapis.com/auth/gmail.modify',
|
|
19
19
|
'https://www.googleapis.com/auth/gmail.send',
|