nothumanallowed 9.7.1 → 9.8.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/ask.mjs +206 -18
- package/src/commands/chat.mjs +482 -64
- package/src/commands/ui.mjs +843 -89
- package/src/constants.mjs +1 -1
- package/src/services/browser-engine.mjs +1240 -0
- package/src/services/conversations.mjs +277 -0
- package/src/services/tool-executor.mjs +384 -59
- package/src/services/web-tools.mjs +430 -0
- package/src/services/web-ui.mjs +422 -173
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.8.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/commands/ask.mjs
CHANGED
|
@@ -18,9 +18,12 @@ export async function cmdAsk(args) {
|
|
|
18
18
|
if (!agentName || agentName.startsWith('-')) {
|
|
19
19
|
fail('Usage: nha ask <agent> "your question"');
|
|
20
20
|
fail(' nha ask saber "Audit this Express app for OWASP Top 10"');
|
|
21
|
-
fail(' nha ask oracle "Analyze this CSV
|
|
21
|
+
fail(' nha ask oracle "Analyze this CSV" --file data.csv');
|
|
22
|
+
fail(' nha ask forge "What\'s in this?" --image screenshot.png');
|
|
22
23
|
console.log('');
|
|
23
24
|
info('Available agents: ' + AGENTS.join(', '));
|
|
25
|
+
info('Custom agents in ~/.nha/agents/ are also available.');
|
|
26
|
+
info('Create one: nha agent:create myagent "Expert in X" "You are..."');
|
|
24
27
|
process.exit(1);
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -28,6 +31,7 @@ export async function cmdAsk(args) {
|
|
|
28
31
|
if (!fs.existsSync(agentFile)) {
|
|
29
32
|
fail(`Agent "${agentName}" not found in ~/.nha/agents/`);
|
|
30
33
|
info('Available: ' + AGENTS.join(', '));
|
|
34
|
+
info('Create custom: nha agent:create <name> <tagline> <system-prompt>');
|
|
31
35
|
process.exit(1);
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -36,32 +40,33 @@ export async function cmdAsk(args) {
|
|
|
36
40
|
let model = null;
|
|
37
41
|
let stream = true;
|
|
38
42
|
let attachFile = null;
|
|
43
|
+
let attachImage = null;
|
|
39
44
|
|
|
40
45
|
for (let i = 1; i < args.length; i++) {
|
|
41
46
|
if (args[i] === '--provider' && args[i + 1]) { provider = args[++i]; continue; }
|
|
42
47
|
if (args[i] === '--model' && args[i + 1]) { model = args[++i]; continue; }
|
|
43
48
|
if (args[i] === '--no-stream') { stream = false; continue; }
|
|
44
49
|
if (args[i] === '--file' && args[i + 1]) { attachFile = args[++i]; continue; }
|
|
50
|
+
if (args[i] === '--image' && args[i + 1]) { attachImage = args[++i]; continue; }
|
|
45
51
|
promptParts.push(args[i]);
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
let userMessage = promptParts.join(' ');
|
|
49
|
-
if (!userMessage) {
|
|
50
|
-
fail('No prompt provided.');
|
|
51
|
-
fail('Usage: nha ask saber "your question here"');
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
55
|
|
|
55
56
|
if (attachFile) {
|
|
56
57
|
const filePath = path.resolve(attachFile);
|
|
57
|
-
if (!fs.existsSync(filePath)) {
|
|
58
|
-
fail(`File not found: ${attachFile}`);
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
58
|
+
if (!fs.existsSync(filePath)) { fail(`File not found: ${attachFile}`); process.exit(1); }
|
|
61
59
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
62
60
|
const maxChars = 100_000;
|
|
63
61
|
const truncated = content.length > maxChars ? content.slice(0, maxChars) + '\n\n[... truncated ...]' : content;
|
|
64
|
-
|
|
62
|
+
info(`Attached: ${path.basename(filePath)} (${Math.round(content.length / 1024)} KB)`);
|
|
63
|
+
userMessage = (userMessage || 'Analyze this file') + `\n\n--- Attached file: ${path.basename(filePath)} ---\n${truncated}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!userMessage && !attachImage) {
|
|
67
|
+
fail('No prompt provided.');
|
|
68
|
+
fail('Usage: nha ask saber "your question here"');
|
|
69
|
+
process.exit(1);
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
const config = loadConfig();
|
|
@@ -85,16 +90,92 @@ export async function cmdAsk(args) {
|
|
|
85
90
|
console.log(`\n ${BOLD}${card?.displayName || agentName.toUpperCase()}${NC} ${D}(${card?.tagline || card?.category || 'agent'})${NC}`);
|
|
86
91
|
console.log(` ${D}Provider: ${provider}${model ? ' / ' + model : ''} | Direct call — no server${NC}\n`);
|
|
87
92
|
|
|
88
|
-
const callFn = getProviderCall(provider);
|
|
89
|
-
if (!callFn) {
|
|
90
|
-
fail(`Unknown provider: ${provider}`);
|
|
91
|
-
info('Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere');
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
93
|
const startTime = Date.now();
|
|
96
94
|
|
|
97
95
|
try {
|
|
96
|
+
// Image attachment — use vision API
|
|
97
|
+
if (attachImage) {
|
|
98
|
+
const imagePath = path.resolve(attachImage);
|
|
99
|
+
if (!fs.existsSync(imagePath)) { fail(`Image not found: ${attachImage}`); process.exit(1); }
|
|
100
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
101
|
+
const base64 = imageBuffer.toString('base64');
|
|
102
|
+
const ext = path.extname(imagePath).toLowerCase();
|
|
103
|
+
const mimeMap = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.webp': 'image/webp', '.gif': 'image/gif' };
|
|
104
|
+
const mimeType = mimeMap[ext] || 'image/jpeg';
|
|
105
|
+
info(`Attached image: ${path.basename(imagePath)} (${Math.round(base64.length * 3 / 4 / 1024)} KB)`);
|
|
106
|
+
|
|
107
|
+
const imagePrompt = userMessage || 'Describe this image in detail. Extract any text, data, or important information.';
|
|
108
|
+
let response = '';
|
|
109
|
+
|
|
110
|
+
if (provider === 'anthropic') {
|
|
111
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
model: model || 'claude-sonnet-4-20250514', max_tokens: 8192, system: systemPrompt,
|
|
116
|
+
messages: [{ role: 'user', content: [
|
|
117
|
+
{ type: 'image', source: { type: 'base64', media_type: mimeType, data: base64 } },
|
|
118
|
+
{ type: 'text', text: imagePrompt },
|
|
119
|
+
]}],
|
|
120
|
+
}),
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok) throw new Error(`Anthropic ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
123
|
+
const data = await res.json();
|
|
124
|
+
response = data.content?.[0]?.text || '';
|
|
125
|
+
} else if (provider === 'openai') {
|
|
126
|
+
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
129
|
+
body: JSON.stringify({
|
|
130
|
+
model: model || 'gpt-4o-mini', max_tokens: 8192,
|
|
131
|
+
messages: [
|
|
132
|
+
{ role: 'system', content: systemPrompt },
|
|
133
|
+
{ role: 'user', content: [
|
|
134
|
+
{ type: 'image_url', image_url: { url: `data:${mimeType};base64,${base64}` } },
|
|
135
|
+
{ type: 'text', text: imagePrompt },
|
|
136
|
+
]},
|
|
137
|
+
],
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
if (!res.ok) throw new Error(`OpenAI ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
141
|
+
const data = await res.json();
|
|
142
|
+
response = data.choices?.[0]?.message?.content || '';
|
|
143
|
+
} else if (provider === 'gemini') {
|
|
144
|
+
const m = model || 'gemini-2.0-flash';
|
|
145
|
+
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${m}:generateContent?key=${apiKey}`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
system_instruction: { parts: [{ text: systemPrompt }] },
|
|
150
|
+
contents: [{ parts: [
|
|
151
|
+
{ inline_data: { mime_type: mimeType, data: base64 } },
|
|
152
|
+
{ text: imagePrompt },
|
|
153
|
+
]}],
|
|
154
|
+
generationConfig: { maxOutputTokens: 8192 },
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok) throw new Error(`Gemini ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
158
|
+
const data = await res.json();
|
|
159
|
+
response = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
160
|
+
} else {
|
|
161
|
+
fail(`Vision not supported for "${provider}". Use anthropic, openai, or gemini.`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(response);
|
|
166
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
167
|
+
console.log(`\n ${D}${elapsed}s | ${provider}${model ? ' / ' + model : ''} | ${card?.displayName || agentName}${NC}\n`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Text / file — standard LLM call
|
|
172
|
+
const callFn = getProviderCall(provider);
|
|
173
|
+
if (!callFn) {
|
|
174
|
+
fail(`Unknown provider: ${provider}`);
|
|
175
|
+
info('Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
98
179
|
const useStream = stream && (provider === 'anthropic' || provider === 'openai' || provider === 'deepseek' || provider === 'grok' || provider === 'mistral');
|
|
99
180
|
const result = await callFn(apiKey, model, systemPrompt, userMessage, useStream);
|
|
100
181
|
|
|
@@ -109,3 +190,110 @@ export async function cmdAsk(args) {
|
|
|
109
190
|
process.exit(1);
|
|
110
191
|
}
|
|
111
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* nha agent:create <name> <tagline> <system-prompt>
|
|
196
|
+
* Creates a custom agent file in ~/.nha/agents/
|
|
197
|
+
*/
|
|
198
|
+
/**
|
|
199
|
+
* nha agent:list — Show all available agents (built-in + custom)
|
|
200
|
+
*/
|
|
201
|
+
export async function cmdAgentList() {
|
|
202
|
+
info('Built-in agents:');
|
|
203
|
+
for (const name of AGENTS) {
|
|
204
|
+
const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
|
|
205
|
+
if (fs.existsSync(agentFile)) {
|
|
206
|
+
const source = fs.readFileSync(agentFile, 'utf-8');
|
|
207
|
+
const { card } = parseAgentFile(source, name);
|
|
208
|
+
console.log(` ${C}${name.padEnd(16)}${NC} ${D}${card?.tagline || card?.category || ''}${NC}`);
|
|
209
|
+
} else {
|
|
210
|
+
console.log(` ${D}${name.padEnd(16)} (not downloaded)${NC}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Custom agents
|
|
215
|
+
if (fs.existsSync(AGENTS_DIR)) {
|
|
216
|
+
const custom = fs.readdirSync(AGENTS_DIR)
|
|
217
|
+
.filter(f => f.endsWith('.mjs'))
|
|
218
|
+
.map(f => f.replace('.mjs', ''))
|
|
219
|
+
.filter(n => !AGENTS.includes(n));
|
|
220
|
+
if (custom.length > 0) {
|
|
221
|
+
console.log(`\n${Y}Custom agents:${NC}`);
|
|
222
|
+
for (const name of custom) {
|
|
223
|
+
const source = fs.readFileSync(path.join(AGENTS_DIR, `${name}.mjs`), 'utf-8');
|
|
224
|
+
const { card } = parseAgentFile(source, name);
|
|
225
|
+
console.log(` ${Y}${name.padEnd(16)}${NC} ${D}${card?.tagline || ''}${NC}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log(`\n ${D}Invoke: nha ask <agent> "prompt"${NC}`);
|
|
230
|
+
console.log(` ${D}Create: nha agent:create <name> "<tagline>" "<system prompt>"${NC}`);
|
|
231
|
+
console.log(` ${D}Delete: nha agent:delete <name>${NC}\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* nha agent:delete <name> — Delete a custom agent
|
|
236
|
+
*/
|
|
237
|
+
export async function cmdAgentDelete(args) {
|
|
238
|
+
const name = (args[0] || '').toLowerCase();
|
|
239
|
+
if (!name) {
|
|
240
|
+
fail('Usage: nha agent:delete <agent-name>');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
if (AGENTS.includes(name)) {
|
|
244
|
+
fail(`"${name}" is a built-in agent and cannot be deleted.`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
|
|
248
|
+
if (!fs.existsSync(agentFile)) {
|
|
249
|
+
fail(`Agent "${name}" not found.`);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
fs.unlinkSync(agentFile);
|
|
253
|
+
ok(`Agent "${name}" deleted.`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function cmdAgentCreate(args) {
|
|
257
|
+
const name = (args[0] || '').toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
258
|
+
const tagline = args[1] || '';
|
|
259
|
+
const sysPrompt = args.slice(2).join(' ') || '';
|
|
260
|
+
|
|
261
|
+
if (!name || !tagline || !sysPrompt) {
|
|
262
|
+
fail('Usage: nha agent:create <name> "<tagline>" "<system prompt>"');
|
|
263
|
+
console.log('');
|
|
264
|
+
info('Example:');
|
|
265
|
+
info(' nha agent:create reviewer "Code review expert" "You are a senior code reviewer..."');
|
|
266
|
+
console.log('');
|
|
267
|
+
info('The agent will be available as: nha ask reviewer "review this code"');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
|
|
272
|
+
if (fs.existsSync(agentFile)) {
|
|
273
|
+
fail(`Agent "${name}" already exists at ${agentFile}`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const content = `// NHA Custom Agent: ${name}
|
|
278
|
+
// Created: ${new Date().toISOString()}
|
|
279
|
+
|
|
280
|
+
export const CARD = {
|
|
281
|
+
name: '${name}',
|
|
282
|
+
displayName: '${name.toUpperCase()}',
|
|
283
|
+
category: 'custom',
|
|
284
|
+
tagline: '${tagline.replace(/'/g, "\\'")}',
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export const SYSTEM_PROMPT = \`${sysPrompt.replace(/`/g, '\\`')}\`;
|
|
288
|
+
`;
|
|
289
|
+
|
|
290
|
+
if (!fs.existsSync(AGENTS_DIR)) {
|
|
291
|
+
fs.mkdirSync(AGENTS_DIR, { recursive: true });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fs.writeFileSync(agentFile, content, 'utf-8');
|
|
295
|
+
ok(`Agent "${name}" created at ${agentFile}`);
|
|
296
|
+
info(`Invoke it: nha ask ${name} "your question"`);
|
|
297
|
+
info(`With file: nha ask ${name} "analyze" --file report.csv`);
|
|
298
|
+
info(`With image: nha ask ${name} "what is this?" --image photo.jpg`);
|
|
299
|
+
}
|