nothumanallowed 11.2.0 → 11.3.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/bootstrap.mjs +7 -3
- package/src/commands/ui.mjs +4 -2
- package/src/constants.mjs +1 -1
- package/src/services/llm.mjs +41 -1
- package/src/services/notification.mjs +11 -5
- package/src/services/web-ui.mjs +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.3.0",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 53 tools. Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, GitHub, Notion, Slack, voice chat, 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/bootstrap.mjs
CHANGED
|
@@ -102,10 +102,14 @@ export async function bootstrap() {
|
|
|
102
102
|
ok('NHA is ready!\n');
|
|
103
103
|
|
|
104
104
|
if (!config.llm.apiKey) {
|
|
105
|
-
console.log(` ${'\x1b[1;33m'}Next step:${'\x1b[0m'}
|
|
105
|
+
console.log(` ${'\x1b[1;33m'}Next step:${'\x1b[0m'} Choose your LLM provider:\n`);
|
|
106
|
+
console.log(` ${'\x1b[1;32m'}Option 1 — NHA Free (no API key needed):${'\x1b[0m'}`);
|
|
107
|
+
console.log(` nha config set provider nha`);
|
|
108
|
+
console.log(` ${'\x1b[2m'}Powered by Liara (Qwen3 32B). Free, slower (5-15s). No key required.${'\x1b[0m'}\n`);
|
|
109
|
+
console.log(` ${'\x1b[1;36m'}Option 2 — Your own API key (faster, more capable):${'\x1b[0m'}`);
|
|
106
110
|
console.log(` nha config set provider anthropic`);
|
|
107
|
-
console.log(` nha config set key sk-ant-api03-YOUR_KEY_HERE
|
|
108
|
-
console.log(`
|
|
111
|
+
console.log(` nha config set key sk-ant-api03-YOUR_KEY_HERE`);
|
|
112
|
+
console.log(` ${'\x1b[2m'}Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere${'\x1b[0m'}\n`);
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
return true;
|
package/src/commands/ui.mjs
CHANGED
|
@@ -1088,7 +1088,9 @@ export async function cmdUI(args) {
|
|
|
1088
1088
|
}
|
|
1089
1089
|
|
|
1090
1090
|
if (!config.llm.apiKey) {
|
|
1091
|
-
|
|
1091
|
+
if (config.llm.provider !== 'nha') {
|
|
1092
|
+
sendJSON(res, 200, { response: 'No API key configured. Run: nha config set key YOUR_KEY\nOr use NHA Free (no key needed): nha config set provider nha', error: 'no_api_key' });
|
|
1093
|
+
}
|
|
1092
1094
|
logRequest(method, pathname, 200, Date.now() - start);
|
|
1093
1095
|
return;
|
|
1094
1096
|
}
|
|
@@ -1496,7 +1498,7 @@ export async function cmdUI(args) {
|
|
|
1496
1498
|
if (method === 'POST' && pathname === '/api/chat/stream') {
|
|
1497
1499
|
const body = await parseBody(req);
|
|
1498
1500
|
if (!body.message) { sendJSON(res, 400, { error: 'message required' }); logRequest(method, pathname, 400, Date.now() - start); return; }
|
|
1499
|
-
if (!config.llm.apiKey) { sendJSON(res, 200, { error: 'no_api_key' }); logRequest(method, pathname, 200, Date.now() - start); return; }
|
|
1501
|
+
if (!config.llm.apiKey && config.llm.provider !== 'nha') { sendJSON(res, 200, { error: 'no_api_key' }); logRequest(method, pathname, 200, Date.now() - start); return; }
|
|
1500
1502
|
|
|
1501
1503
|
const msg = body.message.trim();
|
|
1502
1504
|
const convId = body.conversationId;
|
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 = '11.
|
|
8
|
+
export const VERSION = '11.3.0';
|
|
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/services/llm.mjs
CHANGED
|
@@ -232,7 +232,39 @@ export async function streamSSE(res, format) {
|
|
|
232
232
|
|
|
233
233
|
// ── Router ─────────────────────────────────────────────────────────────────
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* NHA Free (Liara) — free LLM tier, no API key required.
|
|
237
|
+
* Connects to the NHA-hosted Qwen3 + LoRA on Hetzner GPU.
|
|
238
|
+
* OpenAI-compatible format. Slower than paid providers (5-15 seconds).
|
|
239
|
+
*/
|
|
240
|
+
export async function callNHA(apiKey, model, systemPrompt, userMessage, stream = false) {
|
|
241
|
+
const body = {
|
|
242
|
+
model: model || 'nha-v1',
|
|
243
|
+
max_tokens: 4096,
|
|
244
|
+
messages: [
|
|
245
|
+
{ role: 'system', content: systemPrompt },
|
|
246
|
+
{ role: 'user', content: userMessage },
|
|
247
|
+
],
|
|
248
|
+
stream,
|
|
249
|
+
};
|
|
250
|
+
const res = await fetch('https://liara.nothumanallowed.com/v1/chat/completions', {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: {
|
|
253
|
+
'Content-Type': 'application/json',
|
|
254
|
+
},
|
|
255
|
+
body: JSON.stringify(body),
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
const err = await res.text();
|
|
259
|
+
throw new Error(`NHA Free ${res.status}: ${err}`);
|
|
260
|
+
}
|
|
261
|
+
if (stream) return streamSSE(res, 'openai'); // OpenAI-compatible SSE format
|
|
262
|
+
const data = await res.json();
|
|
263
|
+
return data.choices?.[0]?.message?.content || '';
|
|
264
|
+
}
|
|
265
|
+
|
|
235
266
|
const PROVIDERS = {
|
|
267
|
+
nha: callNHA,
|
|
236
268
|
anthropic: callAnthropic,
|
|
237
269
|
openai: callOpenAI,
|
|
238
270
|
gemini: callGemini,
|
|
@@ -247,6 +279,9 @@ export function getProviderCall(provider) {
|
|
|
247
279
|
}
|
|
248
280
|
|
|
249
281
|
export function getApiKey(config, provider) {
|
|
282
|
+
// NHA Free (Liara) doesn't need an API key
|
|
283
|
+
if (provider === 'nha') return 'nha-free-tier';
|
|
284
|
+
|
|
250
285
|
const keyMap = {
|
|
251
286
|
anthropic: config.llm.apiKey,
|
|
252
287
|
openai: config.llm.openaiKey || config.llm.apiKey,
|
|
@@ -417,6 +452,7 @@ function buildRequestBody(provider, model, systemPrompt, userMessage, stream) {
|
|
|
417
452
|
}
|
|
418
453
|
// OpenAI-compatible format (OpenAI, DeepSeek, Grok, Mistral)
|
|
419
454
|
const modelDefaults = {
|
|
455
|
+
nha: 'nha-v1',
|
|
420
456
|
openai: 'gpt-4o',
|
|
421
457
|
deepseek: 'deepseek-chat',
|
|
422
458
|
grok: 'grok-3-latest',
|
|
@@ -424,7 +460,7 @@ function buildRequestBody(provider, model, systemPrompt, userMessage, stream) {
|
|
|
424
460
|
};
|
|
425
461
|
return {
|
|
426
462
|
model: model || modelDefaults[provider] || 'gpt-4o',
|
|
427
|
-
max_tokens: 8192,
|
|
463
|
+
max_tokens: provider === 'nha' ? 4096 : 8192,
|
|
428
464
|
messages: [
|
|
429
465
|
{ role: 'system', content: systemPrompt },
|
|
430
466
|
{ role: 'user', content: userMessage },
|
|
@@ -436,6 +472,7 @@ function buildRequestBody(provider, model, systemPrompt, userMessage, stream) {
|
|
|
436
472
|
/** Get provider API URL */
|
|
437
473
|
function getProviderUrl(provider, model, apiKey) {
|
|
438
474
|
const urls = {
|
|
475
|
+
nha: 'https://liara.nothumanallowed.com/v1/chat/completions',
|
|
439
476
|
anthropic: 'https://api.anthropic.com/v1/messages',
|
|
440
477
|
openai: 'https://api.openai.com/v1/chat/completions',
|
|
441
478
|
deepseek: 'https://api.deepseek.com/v1/chat/completions',
|
|
@@ -454,6 +491,9 @@ function getProviderHeaders(provider, apiKey) {
|
|
|
454
491
|
'anthropic-version': '2023-06-01',
|
|
455
492
|
};
|
|
456
493
|
}
|
|
494
|
+
if (provider === 'nha') {
|
|
495
|
+
return { 'Content-Type': 'application/json' }; // No auth needed for free tier
|
|
496
|
+
}
|
|
457
497
|
return {
|
|
458
498
|
'Content-Type': 'application/json',
|
|
459
499
|
'Authorization': `Bearer ${apiKey}`,
|
|
@@ -46,13 +46,19 @@ function sendDesktop(title, body) {
|
|
|
46
46
|
const platform = os.platform();
|
|
47
47
|
try {
|
|
48
48
|
if (platform === 'darwin') {
|
|
49
|
-
const escaped = body.replace(/"/g, '\\"').slice(0, 200);
|
|
50
|
-
|
|
49
|
+
const escaped = body.replace(/"/g, '\\"').replace(/'/g, "'").slice(0, 200);
|
|
50
|
+
const escapedTitle = 'NHA: ' + title.replace(/"/g, '\\"');
|
|
51
|
+
// Try terminal-notifier first (supports click-to-open)
|
|
52
|
+
try {
|
|
53
|
+
execSync(`which terminal-notifier`, { stdio: 'ignore' });
|
|
54
|
+
execSync(`terminal-notifier -title "${escapedTitle}" -message "${escaped}" -open "http://127.0.0.1:3847" -appIcon "" -group nha 2>/dev/null`);
|
|
55
|
+
} catch {
|
|
56
|
+
// Fallback to osascript — use "NHA" as subtitle so click goes nowhere confusing
|
|
57
|
+
execSync(`osascript -e 'display notification "${escaped}" with title "${escapedTitle}" subtitle "Open nha ui to see details"'`);
|
|
58
|
+
}
|
|
51
59
|
} else if (platform === 'linux') {
|
|
52
60
|
execSync(`notify-send "NHA: ${title}" "${body.slice(0, 200)}"`);
|
|
53
|
-
}
|
|
54
|
-
// Windows: PowerShell toast (best effort)
|
|
55
|
-
else if (platform === 'win32') {
|
|
61
|
+
} else if (platform === 'win32') {
|
|
56
62
|
execSync(`powershell -Command "New-BurntToastNotification -Text 'NHA: ${title}', '${body.slice(0, 200)}'" 2>NUL`);
|
|
57
63
|
}
|
|
58
64
|
} catch { /* non-fatal */ }
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -1979,9 +1979,10 @@ function renderSettings(el) {
|
|
|
1979
1979
|
['role', 'Role', 'e.g. Software Engineer'],
|
|
1980
1980
|
['profile-notes', 'Notes', 'Anything else agents should know about you'],
|
|
1981
1981
|
]) +
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
['
|
|
1982
|
+
'<div style="padding:12px 16px;margin-bottom:16px;background:var(--amberdim);border:1px solid var(--amber3);border-radius:8px"><span style="font-family:var(--term);color:var(--amber);font-size:13px;font-weight:700">NHA Free (Liara)</span><div style="font-size:11px;color:var(--dim);margin:4px 0 8px">Powered by Qwen3 32B. Free, no API key needed. Slower (5-15s).</div><button onclick="apiPost(\\x27/api/config\\x27,{key:\\x27provider\\x27,value:\\x27nha\\x27}).then(function(){location.reload()})" style="padding:6px 16px;background:var(--amber3);color:var(--bg);border:none;border-radius:6px;cursor:pointer;font-family:var(--mono);font-size:11px;font-weight:700">Use NHA Free</button></div>' +
|
|
1983
|
+
settingsSection('llm', 'LLM Provider', 'Or use your own API key for faster, more capable responses.', [
|
|
1984
|
+
['provider', 'Provider', 'nha (free) / anthropic / openai / gemini / deepseek / grok / mistral'],
|
|
1985
|
+
['key', 'API Key', 'Not needed for NHA Free', true],
|
|
1985
1986
|
['model', 'Model', 'Leave empty for default'],
|
|
1986
1987
|
]) +
|
|
1987
1988
|
settingsSection('responder', 'Message Responder', 'Auto-reply to Telegram and Discord messages.', [
|