nothumanallowed 8.8.2 → 8.9.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/ui.mjs +81 -0
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +53 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.9.0",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents + unified productivity suite. Gmail, Calendar, Drive, Contacts, Tasks, GitHub, Notion, Slack, voice chat, smart scheduler. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -764,6 +764,87 @@ export async function cmdUI(args) {
|
|
|
764
764
|
if (memCtx) enrichedSystemPrompt = basePrompt + memCtx;
|
|
765
765
|
} catch { /* memory unavailable */ }
|
|
766
766
|
|
|
767
|
+
// Handle image attachment — vision API
|
|
768
|
+
if (body.imageBase64 && body.imageMimeType) {
|
|
769
|
+
try {
|
|
770
|
+
const provider = config.llm.provider || 'anthropic';
|
|
771
|
+
const apiKey = config.llm.apiKey;
|
|
772
|
+
const model = config.llm.model;
|
|
773
|
+
const imagePrompt = body.message || 'Describe this image in detail. Extract any text or important information.';
|
|
774
|
+
let visionResponse = '';
|
|
775
|
+
|
|
776
|
+
if (provider === 'anthropic') {
|
|
777
|
+
const r = await fetch('https://api.anthropic.com/v1/messages', {
|
|
778
|
+
method: 'POST',
|
|
779
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
|
|
780
|
+
body: JSON.stringify({
|
|
781
|
+
model: model || 'claude-sonnet-4-20250514', max_tokens: 4096, system: enrichedSystemPrompt,
|
|
782
|
+
messages: [{ role: 'user', content: [
|
|
783
|
+
{ type: 'image', source: { type: 'base64', media_type: body.imageMimeType, data: body.imageBase64 } },
|
|
784
|
+
{ type: 'text', text: imagePrompt },
|
|
785
|
+
]}],
|
|
786
|
+
}),
|
|
787
|
+
});
|
|
788
|
+
if (!r.ok) throw new Error(`Anthropic ${r.status}`);
|
|
789
|
+
const d = await r.json();
|
|
790
|
+
visionResponse = d.content?.[0]?.text || '';
|
|
791
|
+
} else if (provider === 'openai') {
|
|
792
|
+
const r = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
793
|
+
method: 'POST',
|
|
794
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
795
|
+
body: JSON.stringify({
|
|
796
|
+
model: model || 'gpt-4o-mini', max_tokens: 4096,
|
|
797
|
+
messages: [
|
|
798
|
+
{ role: 'system', content: enrichedSystemPrompt },
|
|
799
|
+
{ role: 'user', content: [
|
|
800
|
+
{ type: 'image_url', image_url: { url: `data:${body.imageMimeType};base64,${body.imageBase64}` } },
|
|
801
|
+
{ type: 'text', text: imagePrompt },
|
|
802
|
+
]},
|
|
803
|
+
],
|
|
804
|
+
}),
|
|
805
|
+
});
|
|
806
|
+
if (!r.ok) throw new Error(`OpenAI ${r.status}`);
|
|
807
|
+
const d = await r.json();
|
|
808
|
+
visionResponse = d.choices?.[0]?.message?.content || '';
|
|
809
|
+
} else if (provider === 'gemini') {
|
|
810
|
+
const m = model || 'gemini-2.0-flash';
|
|
811
|
+
const r = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${m}:generateContent?key=${apiKey}`, {
|
|
812
|
+
method: 'POST',
|
|
813
|
+
headers: { 'Content-Type': 'application/json' },
|
|
814
|
+
body: JSON.stringify({
|
|
815
|
+
system_instruction: { parts: [{ text: enrichedSystemPrompt }] },
|
|
816
|
+
contents: [{ parts: [
|
|
817
|
+
{ inline_data: { mime_type: body.imageMimeType, data: body.imageBase64 } },
|
|
818
|
+
{ text: imagePrompt },
|
|
819
|
+
]}],
|
|
820
|
+
generationConfig: { maxOutputTokens: 4096 },
|
|
821
|
+
}),
|
|
822
|
+
});
|
|
823
|
+
if (!r.ok) throw new Error(`Gemini ${r.status}`);
|
|
824
|
+
const d = await r.json();
|
|
825
|
+
visionResponse = d.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
826
|
+
} else {
|
|
827
|
+
visionResponse = `Vision not supported for provider "${provider}". Use anthropic, openai, or gemini.`;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
sendJSON(res, 200, { response: visionResponse });
|
|
831
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
832
|
+
return;
|
|
833
|
+
} catch (e) {
|
|
834
|
+
sendJSON(res, 200, { response: null, error: e.message });
|
|
835
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Handle file attachment
|
|
841
|
+
if (body.fileContent && body.fileName) {
|
|
842
|
+
const filePrompt = body.message
|
|
843
|
+
? `User asks about file "${body.fileName}": ${body.message}\n\nFile content:\n${body.fileContent.slice(0, 8000)}`
|
|
844
|
+
: `Analyze this file "${body.fileName}":\n\n${body.fileContent.slice(0, 8000)}`;
|
|
845
|
+
userMessage = filePrompt;
|
|
846
|
+
}
|
|
847
|
+
|
|
767
848
|
try {
|
|
768
849
|
const response = await callLLM(config, enrichedSystemPrompt, userMessage);
|
|
769
850
|
const { textParts, actions } = parseActions(response);
|
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 = '8.
|
|
8
|
+
export const VERSION = '8.9.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/web-ui.mjs
CHANGED
|
@@ -316,7 +316,7 @@ function renderDash(el){
|
|
|
316
316
|
var chatReady=false;
|
|
317
317
|
function renderChat(el){
|
|
318
318
|
if(!chatReady||!document.getElementById('chatMessages')){
|
|
319
|
-
el.innerHTML='<div class="chat"><div class="chat__messages" id="chatMessages"></div><div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">🎤</button><textarea class="chat__input" id="chatInput" placeholder="Ask anything..." rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button onclick="clearChatHistory()" style="background:none;color:var(--dim);font-size:10px;padding:4px 8px" title="Clear chat history">Clear</button></div></div>';
|
|
319
|
+
el.innerHTML='<div class="chat"><div class="chat__messages" id="chatMessages"></div><div id="chatAttachInfo" style="display:none;padding:4px 12px;font-size:11px;color:var(--cyan);background:var(--bg2);border-top:1px solid var(--border)"><span id="chatAttachName"></span> <button onclick="clearChatAttach()" style="background:none;border:none;color:#f44;cursor:pointer;font-size:14px;font-weight:700">×</button></div><div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">🎤</button><button onclick="document.getElementById(\\x27chatFileInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach file">📎</button><button onclick="document.getElementById(\\x27chatImageInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach image">📷</button><input type="file" id="chatFileInput" style="display:none" onchange="handleChatFile(this)"><input type="file" id="chatImageInput" accept="image/*" style="display:none" onchange="handleChatImage(this)"><textarea class="chat__input" id="chatInput" placeholder="Ask anything... (or attach file/image first)" rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button onclick="clearChatHistory()" style="background:none;color:var(--dim);font-size:10px;padding:4px 8px" title="Clear chat history">Clear</button></div></div>';
|
|
320
320
|
chatReady=true;
|
|
321
321
|
document.getElementById('chatSend').onclick=sendChat;
|
|
322
322
|
document.getElementById('chatInput').onkeydown=function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendChat()}};
|
|
@@ -335,26 +335,68 @@ function renderMessages(){
|
|
|
335
335
|
});
|
|
336
336
|
el.innerHTML=h;el.scrollTop=el.scrollHeight;
|
|
337
337
|
}
|
|
338
|
+
var chatAttachedFile=null;
|
|
339
|
+
var chatAttachedImage=null;
|
|
340
|
+
|
|
341
|
+
function handleChatFile(input){
|
|
342
|
+
var file=input.files&&input.files[0];if(!file)return;
|
|
343
|
+
var reader=new FileReader();
|
|
344
|
+
reader.onload=function(e){
|
|
345
|
+
chatAttachedFile={name:file.name,size:file.size,content:e.target.result};
|
|
346
|
+
chatAttachedImage=null;
|
|
347
|
+
document.getElementById('chatAttachInfo').style.display='';
|
|
348
|
+
document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
|
|
349
|
+
};
|
|
350
|
+
reader.readAsText(file);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function handleChatImage(input){
|
|
354
|
+
var file=input.files&&input.files[0];if(!file)return;
|
|
355
|
+
var reader=new FileReader();
|
|
356
|
+
reader.onload=function(e){
|
|
357
|
+
var base64=e.target.result.split(',')[1];
|
|
358
|
+
chatAttachedImage={name:file.name,size:file.size,base64:base64,mimeType:file.type||'image/jpeg'};
|
|
359
|
+
chatAttachedFile=null;
|
|
360
|
+
document.getElementById('chatAttachInfo').style.display='';
|
|
361
|
+
document.getElementById('chatAttachName').textContent='📷 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
|
|
362
|
+
};
|
|
363
|
+
reader.readAsDataURL(file);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function clearChatAttach(){
|
|
367
|
+
chatAttachedFile=null;chatAttachedImage=null;
|
|
368
|
+
document.getElementById('chatAttachInfo').style.display='none';
|
|
369
|
+
document.getElementById('chatFileInput').value='';
|
|
370
|
+
document.getElementById('chatImageInput').value='';
|
|
371
|
+
}
|
|
372
|
+
|
|
338
373
|
function sendChat(){
|
|
339
374
|
var inp=document.getElementById('chatInput');if(!inp)return;
|
|
340
|
-
var msg=inp.value.trim();
|
|
341
|
-
|
|
375
|
+
var msg=inp.value.trim();
|
|
376
|
+
var hasAttach=!!chatAttachedFile||!!chatAttachedImage;
|
|
377
|
+
if(!msg&&!hasAttach)return;
|
|
378
|
+
|
|
379
|
+
var displayMsg=msg;
|
|
380
|
+
if(chatAttachedFile)displayMsg=(msg?msg+' ':'')+'[File: '+chatAttachedFile.name+']';
|
|
381
|
+
if(chatAttachedImage)displayMsg=(msg?msg+' ':'')+'[Image: '+chatAttachedImage.name+']';
|
|
382
|
+
|
|
383
|
+
chatHistory.push({role:'user',content:displayMsg});
|
|
342
384
|
inp.value='';saveChatToStorage();renderMessages();
|
|
343
385
|
chatHistory.push({role:'assistant',content:'Thinking...'});renderMessages();
|
|
344
|
-
|
|
386
|
+
|
|
387
|
+
var payload={message:msg||'Analyze this attachment',history:chatHistory.slice(0,-1)};
|
|
388
|
+
if(chatAttachedFile){payload.fileContent=chatAttachedFile.content;payload.fileName=chatAttachedFile.name;}
|
|
389
|
+
if(chatAttachedImage){payload.imageBase64=chatAttachedImage.base64;payload.imageMimeType=chatAttachedImage.mimeType;payload.imageName=chatAttachedImage.name;}
|
|
390
|
+
clearChatAttach();
|
|
391
|
+
|
|
392
|
+
apiPost('/api/chat',payload).then(function(r){
|
|
345
393
|
chatHistory.pop();
|
|
346
394
|
if(r&&r.response){chatHistory.push({role:'assistant',content:r.response})}
|
|
347
395
|
else if(r&&r.error){chatHistory.push({role:'assistant',content:'Error: '+r.error})}
|
|
348
396
|
else{chatHistory.push({role:'assistant',content:'Error: no response from server'})}
|
|
349
397
|
saveChatToStorage();renderMessages();
|
|
350
|
-
// Refresh ALL data after any tool execution
|
|
351
398
|
if(r&&((r.actions&&r.actions.length>0)||(r.toolResults&&r.toolResults.length>0))){
|
|
352
|
-
calEventsCache={};
|
|
353
|
-
contactsData=null;
|
|
354
|
-
notesData=null;
|
|
355
|
-
driveData=null;
|
|
356
|
-
onedriveData=null;
|
|
357
|
-
mstodoData=null;
|
|
399
|
+
calEventsCache={};contactsData=null;notesData=null;driveData=null;onedriveData=null;mstodoData=null;
|
|
358
400
|
loadDash().then(function(){render()}).catch(function(){});
|
|
359
401
|
}
|
|
360
402
|
});
|