samarthya-bot 1.1.4 → 2.0.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/README.md +274 -98
- package/backend/.env.example +31 -7
- package/backend/bin/samarthya.js +114 -25
- package/backend/package-lock.json +47 -25
- package/backend/package.json +4 -3
- package/backend/public/assets/index-BFRAq8Y1.js +149 -0
- package/backend/public/index.html +1 -1
- package/backend/server.js +70 -22
- package/backend/server.log +29 -0
- package/backend/services/agent/spawnService.js +140 -0
- package/backend/services/discord/discordService.js +188 -0
- package/backend/services/heartbeat/heartbeatService.js +157 -0
- package/backend/services/llm/llmService.js +118 -1
- package/backend/services/security/sandboxService.js +115 -0
- package/backend/services/voice/voiceService.js +151 -0
- package/package.json +2 -2
- package/server.log +35 -0
|
@@ -118,7 +118,13 @@ NEVER SAY YOU PERFORMED AN ACTION (like sending an email or saving a file) WITHO
|
|
|
118
118
|
} else if (provider === 'mistral') {
|
|
119
119
|
return this.chatOpenAICompatible(messages, systemPrompt, user, 'https://api.mistral.ai/v1/chat/completions', process.env.MISTRAL_API_KEY, customModel || 'mistral-large-latest');
|
|
120
120
|
} else if (provider === 'anthropic') {
|
|
121
|
-
return this.
|
|
121
|
+
return this.chatAnthropic(messages, systemPrompt, user, customModel || 'claude-sonnet-4-20250514');
|
|
122
|
+
} else if (provider === 'deepseek') {
|
|
123
|
+
return this.chatOpenAICompatible(messages, systemPrompt, user, 'https://api.deepseek.com/v1/chat/completions', process.env.DEEPSEEK_API_KEY, customModel || 'deepseek-chat');
|
|
124
|
+
} else if (provider === 'qwen') {
|
|
125
|
+
return this.chatOpenAICompatible(messages, systemPrompt, user, 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', process.env.QWEN_API_KEY, customModel || 'qwen-plus');
|
|
126
|
+
} else if (provider === 'openrouter') {
|
|
127
|
+
return this.chatOpenRouter(messages, systemPrompt, user, customModel || 'google/gemini-2.5-flash');
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
return this.chatGemini(messages, systemPrompt, user, customModel || 'gemini-2.5-flash');
|
|
@@ -292,6 +298,117 @@ NEVER SAY YOU PERFORMED AN ACTION (like sending an email or saving a file) WITHO
|
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Anthropic Claude API (Native Messages API)
|
|
303
|
+
* Claude uses a different API format than OpenAI
|
|
304
|
+
*/
|
|
305
|
+
async chatAnthropic(messages, systemPrompt, user = null, modelName = 'claude-sonnet-4-20250514') {
|
|
306
|
+
try {
|
|
307
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
308
|
+
if (!apiKey || apiKey === 'dummy') {
|
|
309
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Claude uses a different message format
|
|
313
|
+
const claudeMessages = messages
|
|
314
|
+
.filter(m => m.role !== 'system')
|
|
315
|
+
.map(m => ({
|
|
316
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
317
|
+
content: m.content
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
// Ensure first message is from user
|
|
321
|
+
if (claudeMessages.length === 0 || claudeMessages[0].role !== 'user') {
|
|
322
|
+
claudeMessages.unshift({ role: 'user', content: 'Hello' });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
326
|
+
method: 'POST',
|
|
327
|
+
headers: {
|
|
328
|
+
'Content-Type': 'application/json',
|
|
329
|
+
'x-api-key': apiKey,
|
|
330
|
+
'anthropic-version': '2023-06-01'
|
|
331
|
+
},
|
|
332
|
+
body: JSON.stringify({
|
|
333
|
+
model: modelName,
|
|
334
|
+
max_tokens: 8192,
|
|
335
|
+
system: systemPrompt,
|
|
336
|
+
messages: claudeMessages,
|
|
337
|
+
temperature: 0.7
|
|
338
|
+
})
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (!response.ok) {
|
|
342
|
+
const errText = await response.text();
|
|
343
|
+
console.error('Anthropic API Error:', errText);
|
|
344
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const data = await response.json();
|
|
348
|
+
const textContent = data.content?.find(c => c.type === 'text');
|
|
349
|
+
return {
|
|
350
|
+
content: textContent?.text || 'Empty response from Claude',
|
|
351
|
+
tokensUsed: (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0),
|
|
352
|
+
model: modelName
|
|
353
|
+
};
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error('Anthropic Error:', error.message);
|
|
356
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* OpenRouter API — access 100+ models through a single API key
|
|
362
|
+
* Uses OpenAI-compatible format with extra headers
|
|
363
|
+
*/
|
|
364
|
+
async chatOpenRouter(messages, systemPrompt, user = null, modelName = 'google/gemini-2.5-flash') {
|
|
365
|
+
try {
|
|
366
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
367
|
+
if (!apiKey || apiKey === 'dummy') {
|
|
368
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const apiMessages = [
|
|
372
|
+
{ role: 'system', content: systemPrompt },
|
|
373
|
+
...messages.map(m => ({
|
|
374
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
375
|
+
content: m.content
|
|
376
|
+
}))
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
headers: {
|
|
382
|
+
'Content-Type': 'application/json',
|
|
383
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
384
|
+
'HTTP-Referer': 'https://github.com/mebishnusahu0595/SamarthyaBot',
|
|
385
|
+
'X-Title': 'SamarthyaBot'
|
|
386
|
+
},
|
|
387
|
+
body: JSON.stringify({
|
|
388
|
+
model: modelName,
|
|
389
|
+
messages: apiMessages,
|
|
390
|
+
temperature: 0.7
|
|
391
|
+
})
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
const errText = await response.text();
|
|
396
|
+
console.error('OpenRouter API Error:', errText);
|
|
397
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const data = await response.json();
|
|
401
|
+
return {
|
|
402
|
+
content: data.choices?.[0]?.message?.content || 'Empty response from OpenRouter',
|
|
403
|
+
tokensUsed: data.usage?.total_tokens || 0,
|
|
404
|
+
model: `openrouter:${modelName}`
|
|
405
|
+
};
|
|
406
|
+
} catch (error) {
|
|
407
|
+
console.error('OpenRouter Error:', error.message);
|
|
408
|
+
return this.getFallbackResponse(messages[messages.length - 1]?.content, user);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
295
412
|
/**
|
|
296
413
|
* Screen Understanding via Gemini Vision
|
|
297
414
|
* Takes a base64 screenshot and analyzes it
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Security Sandbox for SamarthyaBot
|
|
3
|
+
* Restricts file and command operations to the configured workspace
|
|
4
|
+
* Inspired by PicoClaw's restrict_to_workspace feature
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
class SandboxService {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.enabled = process.env.RESTRICT_TO_WORKSPACE !== 'false'; // default: true
|
|
13
|
+
this.workspace = process.env.WORKSPACE_PATH || path.join(os.homedir(), 'SamarthyaBot_Files');
|
|
14
|
+
|
|
15
|
+
// Dangerous command patterns (always blocked, even if sandbox is off)
|
|
16
|
+
this.blockedPatterns = [
|
|
17
|
+
/rm\s+(-rf|-fr|--no-preserve-root)\s/i,
|
|
18
|
+
/del\s+\/f/i,
|
|
19
|
+
/rmdir\s+\/s/i,
|
|
20
|
+
/format\s+/i,
|
|
21
|
+
/mkfs\s/i,
|
|
22
|
+
/diskpart/i,
|
|
23
|
+
/dd\s+if=/i,
|
|
24
|
+
/\/dev\/sd[a-z]/i,
|
|
25
|
+
/shutdown/i,
|
|
26
|
+
/reboot/i,
|
|
27
|
+
/poweroff/i,
|
|
28
|
+
/init\s+[06]/i,
|
|
29
|
+
/:()\{\s*:\|:&\s*\};:/, // Fork bomb
|
|
30
|
+
/>\s*\/dev\/sda/i,
|
|
31
|
+
/mv\s+\/\s/i, // mv / (moving root)
|
|
32
|
+
/chmod\s+-R\s+777\s+\//i, // chmod 777 on root
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate that a file path is within the allowed workspace
|
|
38
|
+
* @param {string} filePath - The file path to validate
|
|
39
|
+
* @returns {{ allowed: boolean, reason: string }}
|
|
40
|
+
*/
|
|
41
|
+
validatePath(filePath) {
|
|
42
|
+
if (!this.enabled) return { allowed: true, reason: 'Sandbox disabled' };
|
|
43
|
+
|
|
44
|
+
const resolvedPath = path.resolve(filePath);
|
|
45
|
+
const resolvedWorkspace = path.resolve(this.workspace);
|
|
46
|
+
|
|
47
|
+
if (resolvedPath.startsWith(resolvedWorkspace)) {
|
|
48
|
+
return { allowed: true, reason: 'Path is within workspace' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Allow /tmp for scratch files
|
|
52
|
+
if (resolvedPath.startsWith('/tmp') || resolvedPath.startsWith(os.tmpdir())) {
|
|
53
|
+
return { allowed: true, reason: 'Path is in temp directory' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
allowed: false,
|
|
58
|
+
reason: `🔒 Security: Path "${filePath}" is outside the workspace. Only files within "${this.workspace}" can be accessed. Set RESTRICT_TO_WORKSPACE=false in .env to disable this restriction.`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Validate that a command is safe to execute
|
|
64
|
+
* @param {string} command - The shell command to validate
|
|
65
|
+
* @returns {{ allowed: boolean, reason: string }}
|
|
66
|
+
*/
|
|
67
|
+
validateCommand(command) {
|
|
68
|
+
// Always check blocked patterns (even if sandbox is disabled)
|
|
69
|
+
for (const pattern of this.blockedPatterns) {
|
|
70
|
+
if (pattern.test(command)) {
|
|
71
|
+
return {
|
|
72
|
+
allowed: false,
|
|
73
|
+
reason: `🛡️ Safety Guard: Command blocked — dangerous pattern detected ("${command.substring(0, 50)}..."). This command could harm your system.`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If sandbox is enabled, check that command paths are within workspace
|
|
79
|
+
if (this.enabled) {
|
|
80
|
+
// Extract paths from command (basic heuristic)
|
|
81
|
+
const pathRegex = /(?:^|\s)(\/[^\s;|&]+)/g;
|
|
82
|
+
let match;
|
|
83
|
+
while ((match = pathRegex.exec(command)) !== null) {
|
|
84
|
+
const cmdPath = match[1];
|
|
85
|
+
// Skip common system binaries
|
|
86
|
+
if (cmdPath.startsWith('/usr/') || cmdPath.startsWith('/bin/') ||
|
|
87
|
+
cmdPath.startsWith('/sbin/') || cmdPath.startsWith('/tmp/')) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const validation = this.validatePath(cmdPath);
|
|
91
|
+
if (!validation.allowed) {
|
|
92
|
+
return {
|
|
93
|
+
allowed: false,
|
|
94
|
+
reason: `🔒 Security: Command references path outside workspace — ${validation.reason}`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { allowed: true, reason: 'Command is safe' };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get workspace info for status display
|
|
105
|
+
*/
|
|
106
|
+
getStatus() {
|
|
107
|
+
return {
|
|
108
|
+
enabled: this.enabled,
|
|
109
|
+
workspace: this.workspace,
|
|
110
|
+
blockedPatterns: this.blockedPatterns.length
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = new SandboxService();
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice Transcription Service for SamarthyaBot
|
|
3
|
+
* Uses Groq Whisper API to convert voice notes to text
|
|
4
|
+
* Supports OGG (Telegram), MP3, WAV, M4A formats
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class VoiceService {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.groqApiKey = process.env.GROQ_API_KEY;
|
|
13
|
+
this.model = 'whisper-large-v3-turbo'; // Groq's fast Whisper model
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Transcribe an audio file using Groq Whisper API
|
|
18
|
+
* @param {string|Buffer} audioInput - File path or Buffer of audio data
|
|
19
|
+
* @param {string} fileName - Original filename (for MIME type detection)
|
|
20
|
+
* @param {string} language - Language hint (e.g., 'hi' for Hindi, 'en' for English)
|
|
21
|
+
* @returns {Promise<{text: string, language: string, duration: number}>}
|
|
22
|
+
*/
|
|
23
|
+
async transcribe(audioInput, fileName = 'audio.ogg', language = null) {
|
|
24
|
+
if (!this.groqApiKey || this.groqApiKey === 'your_groq_api_key') {
|
|
25
|
+
return {
|
|
26
|
+
text: null,
|
|
27
|
+
error: 'Voice transcription not available — GROQ_API_KEY not configured. Get a free key at https://console.groq.com'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
let audioBuffer;
|
|
33
|
+
if (typeof audioInput === 'string') {
|
|
34
|
+
// It's a file path
|
|
35
|
+
audioBuffer = fs.readFileSync(audioInput);
|
|
36
|
+
} else {
|
|
37
|
+
audioBuffer = audioInput;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Build multipart form data manually (no external dependency)
|
|
41
|
+
const boundary = '----SamarthyaBotVoice' + Date.now();
|
|
42
|
+
const mimeType = this.getMimeType(fileName);
|
|
43
|
+
|
|
44
|
+
const bodyParts = [];
|
|
45
|
+
|
|
46
|
+
// File part
|
|
47
|
+
bodyParts.push(
|
|
48
|
+
`--${boundary}\r\n`,
|
|
49
|
+
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`,
|
|
50
|
+
`Content-Type: ${mimeType}\r\n\r\n`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const headerBuffer = Buffer.from(bodyParts.join(''));
|
|
54
|
+
const footerParts = [`\r\n--${boundary}\r\n`,
|
|
55
|
+
`Content-Disposition: form-data; name="model"\r\n\r\n`,
|
|
56
|
+
`${this.model}\r\n`
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
if (language) {
|
|
60
|
+
footerParts.push(
|
|
61
|
+
`--${boundary}\r\n`,
|
|
62
|
+
`Content-Disposition: form-data; name="language"\r\n\r\n`,
|
|
63
|
+
`${language}\r\n`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
footerParts.push(
|
|
68
|
+
`--${boundary}\r\n`,
|
|
69
|
+
`Content-Disposition: form-data; name="response_format"\r\n\r\n`,
|
|
70
|
+
`verbose_json\r\n`,
|
|
71
|
+
`--${boundary}--\r\n`
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const footerBuffer = Buffer.from(footerParts.join(''));
|
|
75
|
+
const body = Buffer.concat([headerBuffer, audioBuffer, footerBuffer]);
|
|
76
|
+
|
|
77
|
+
const response = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
'Authorization': `Bearer ${this.groqApiKey}`,
|
|
81
|
+
'Content-Type': `multipart/form-data; boundary=${boundary}`
|
|
82
|
+
},
|
|
83
|
+
body: body
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const errText = await response.text();
|
|
88
|
+
console.error('❌ Voice transcription error:', errText);
|
|
89
|
+
return { text: null, error: `Transcription failed: ${response.status}` };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
console.log(`🎙️ Voice transcribed: "${(data.text || '').substring(0, 60)}..." (${data.language || 'auto'}, ${Math.round(data.duration || 0)}s)`);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
text: data.text || '',
|
|
97
|
+
language: data.language || 'unknown',
|
|
98
|
+
duration: data.duration || 0
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('❌ Voice service error:', error.message);
|
|
102
|
+
return { text: null, error: error.message };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Download a file from URL and transcribe it (for Telegram voice notes)
|
|
108
|
+
* @param {string} fileUrl - URL of the audio file
|
|
109
|
+
* @param {string} language - Language hint
|
|
110
|
+
*/
|
|
111
|
+
async transcribeFromUrl(fileUrl, language = null) {
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(fileUrl);
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
return { text: null, error: `Failed to download audio: ${response.status}` };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
119
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
120
|
+
|
|
121
|
+
// Extract filename from URL
|
|
122
|
+
const urlParts = fileUrl.split('/');
|
|
123
|
+
const fileName = urlParts[urlParts.length - 1] || 'audio.oga';
|
|
124
|
+
|
|
125
|
+
return this.transcribe(buffer, fileName, language);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error('❌ Voice download error:', error.message);
|
|
128
|
+
return { text: null, error: error.message };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getMimeType(fileName) {
|
|
133
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
134
|
+
const mimeMap = {
|
|
135
|
+
'.ogg': 'audio/ogg',
|
|
136
|
+
'.oga': 'audio/ogg',
|
|
137
|
+
'.mp3': 'audio/mpeg',
|
|
138
|
+
'.wav': 'audio/wav',
|
|
139
|
+
'.m4a': 'audio/m4a',
|
|
140
|
+
'.webm': 'audio/webm',
|
|
141
|
+
'.flac': 'audio/flac'
|
|
142
|
+
};
|
|
143
|
+
return mimeMap[ext] || 'audio/ogg';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
isAvailable() {
|
|
147
|
+
return !!(this.groqApiKey && this.groqApiKey !== 'your_groq_api_key');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = new VoiceService();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "samarthya-bot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Privacy-First Local Agentic OS & Command Center",
|
|
5
5
|
"main": "backend/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"type": "git",
|
|
48
48
|
"url": "https://github.com/mebishnusahu0595/SamarthyaBot.git"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/server.log
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
> samarthya-agent@1.0.0 start
|
|
3
|
+
> node server.js
|
|
4
|
+
|
|
5
|
+
[dotenv@17.3.1] injecting env (0) from .env -- tip: 🤖 agentic secret storage: https://dotenvx.com/as2
|
|
6
|
+
✅ MongoDB Connected: localhost
|
|
7
|
+
|
|
8
|
+
╔══════════════════════════════════════════════════════╗
|
|
9
|
+
║ ║
|
|
10
|
+
║ 🧠 SamarthyaBot Server v1.1.0 ║
|
|
11
|
+
║ Privacy-first Personal AI Operator ║
|
|
12
|
+
║ ║
|
|
13
|
+
║ 🌐 Server: http://localhost:5000 ║
|
|
14
|
+
║ 📡 Socket: ws://localhost:5000 ║
|
|
15
|
+
║ 🔗 Health: http://localhost:5000/api/health ║
|
|
16
|
+
║ 📱 WhatsApp: /api/whatsapp/webhook ║
|
|
17
|
+
║ 🤖 Telegram: /api/telegram/webhook ║
|
|
18
|
+
║ 👁️ Vision: /api/screen/analyze ║
|
|
19
|
+
║ ║
|
|
20
|
+
║ 🇮🇳 Built for Indian Workflows ║
|
|
21
|
+
║ 📦 Ollama: ❌ Disabled ║
|
|
22
|
+
║ 🔄 Autonomous Background Engine: ✅ Active ║
|
|
23
|
+
║ 🔌 Dynamic Plugin Engine: ✅ Active ║
|
|
24
|
+
║ 🧠 Active AI: GEMINI (gemini-2.5-flash) ║
|
|
25
|
+
║ ║
|
|
26
|
+
╚══════════════════════════════════════════════════════╝
|
|
27
|
+
|
|
28
|
+
🔄 Background Autonomous Mode Started (Checking every 1 minute)
|
|
29
|
+
[0mGET /api/health [32m200[0m 13.413 ms - 121[0m
|
|
30
|
+
[0mGET / [32m200[0m 5.827 ms - 947[0m
|
|
31
|
+
[0mGET /assets/index-J7XSVHCz.css [32m200[0m 3.896 ms - 7514[0m
|
|
32
|
+
[0mGET /assets/index-BFRAq8Y1.js [32m200[0m 5.924 ms - 409640[0m
|
|
33
|
+
[0mGET /logo.png [32m200[0m 1.936 ms - 395392[0m
|
|
34
|
+
[0mGET /api/auth/profile [32m200[0m 31.161 ms - 457[0m
|
|
35
|
+
[0mGET /favicon.svg [32m200[0m 1.891 ms - 847[0m
|