samarthya-bot 1.0.2
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 +92 -0
- package/backend/.env.example +23 -0
- package/backend/bin/samarthya.js +384 -0
- package/backend/config/constants.js +71 -0
- package/backend/config/db.js +13 -0
- package/backend/controllers/auditController.js +86 -0
- package/backend/controllers/authController.js +154 -0
- package/backend/controllers/chatController.js +158 -0
- package/backend/controllers/fileController.js +268 -0
- package/backend/controllers/platformController.js +54 -0
- package/backend/controllers/screenController.js +91 -0
- package/backend/controllers/telegramController.js +120 -0
- package/backend/controllers/toolsController.js +56 -0
- package/backend/controllers/whatsappController.js +214 -0
- package/backend/fix_toolRegistry.js +25 -0
- package/backend/middleware/auth.js +28 -0
- package/backend/models/AuditLog.js +28 -0
- package/backend/models/BackgroundJob.js +13 -0
- package/backend/models/Conversation.js +40 -0
- package/backend/models/Memory.js +17 -0
- package/backend/models/User.js +24 -0
- package/backend/package-lock.json +3766 -0
- package/backend/package.json +41 -0
- package/backend/public/assets/index-Ckf0GO1B.css +1 -0
- package/backend/public/assets/index-Do4jNsZS.js +19 -0
- package/backend/public/assets/index-Ui-pyZvK.js +25 -0
- package/backend/public/favicon.svg +17 -0
- package/backend/public/index.html +18 -0
- package/backend/public/manifest.json +16 -0
- package/backend/routes/audit.js +9 -0
- package/backend/routes/auth.js +11 -0
- package/backend/routes/chat.js +11 -0
- package/backend/routes/files.js +14 -0
- package/backend/routes/platform.js +18 -0
- package/backend/routes/screen.js +10 -0
- package/backend/routes/telegram.js +8 -0
- package/backend/routes/tools.js +9 -0
- package/backend/routes/whatsapp.js +11 -0
- package/backend/server.js +134 -0
- package/backend/services/background/backgroundService.js +81 -0
- package/backend/services/llm/llmService.js +444 -0
- package/backend/services/memory/memoryService.js +159 -0
- package/backend/services/planner/plannerService.js +182 -0
- package/backend/services/security/securityService.js +166 -0
- package/backend/services/telegram/telegramService.js +49 -0
- package/backend/services/tools/toolRegistry.js +879 -0
- package/backend/services/whatsapp/whatsappService.js +254 -0
- package/backend/test_email.js +29 -0
- package/backend/test_parser.js +10 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>ЁЯза SamarthyaBot</h1>
|
|
3
|
+
<p><strong>Privacy-First Local Agentic OS & Command Center</strong></p>
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
An intelligent, extensible, and localized AI operator built for local-first execution. Out-of-the-box support for API usage (Gemini) or completely offline execution via Ollama. It runs commands, handles background jobs, remembers you securely, and performs multi-step autonomous planning.
|
|
7
|
+
</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<hr/>
|
|
11
|
+
|
|
12
|
+
## ЁЯЪА The Vision: A Developer's Platform, Not Just a Chatbot
|
|
13
|
+
|
|
14
|
+
Most AI tools are single-turn chat interfaces. SamarthyaBot is built as a **Platform OS**. It features a modular Plugin Engine, a Background CRON executor, and a dedicated UI Control Center.
|
|
15
|
+
|
|
16
|
+
You don't just ask SamarthyaBot questions; you *delegate tasks* to it.
|
|
17
|
+
|
|
18
|
+
## ЁЯМЯ Key "Level 2" Features
|
|
19
|
+
|
|
20
|
+
- ЁЯЫС **Control Center UI**: A full dashboard monitoring the health of the agent, loaded plugins, RAM usage, and autonomous loops.
|
|
21
|
+
- ЁЯФБ **Autonomous Background Engine**: Schedule tasks (e.g. "Monitor this folder every 10 mins") and let the agent work silently in the background. Features a **Big Red Kill Switch** for emergency halts.
|
|
22
|
+
- ЁЯФМ **Dynamic Plugin Marketplace**: Drop a `.js` file into `~/SamarthyaBot_Files/plugins/`, and the agent automatically learns the new tool on start.
|
|
23
|
+
- ЁЯУ╕ **Screen Understanding**: Native capability to capture desktop screenshots and pipe them into Vision models.
|
|
24
|
+
- ЁЯФР **Encrypted Memory Vault**: AES-256-CBC local encryption for any API keys, tokens, or PII the agent decides it needs to remember.
|
|
25
|
+
- ЁЯЗоЁЯЗ│ **Indian Context**: Native prompt handling for GST due dates, UPI link generation, and Hinglish/Hindi fluency.
|
|
26
|
+
|
|
27
|
+
## ЁЯЫая╕П Architecture
|
|
28
|
+
|
|
29
|
+
It uses a monolithic structure right now, preparing for a modular NPM package evolution:
|
|
30
|
+
- **Backend**: Node.js, Express, MongoDB (Local), PM2 for Daemonizing.
|
|
31
|
+
- **Frontend**: React, Vite, Lucide-Icons.
|
|
32
|
+
- **Agent Loop**: A custom ReAct-inspired (`Reason`, `Act`, `Observe`) multi-step autonomous planner.
|
|
33
|
+
|
|
34
|
+
## ЁЯУж Quick Start Guide
|
|
35
|
+
|
|
36
|
+
**Prerequisites:** Node.js (v20+), MongoDB running locally, Git.
|
|
37
|
+
|
|
38
|
+
1. **Clone & Install:**
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/mebishnusahu0595/SamarthyaBot.git
|
|
41
|
+
cd SamarthyaBot/backend
|
|
42
|
+
npm install
|
|
43
|
+
cd ../frontend
|
|
44
|
+
npm install
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
2. **Environment Variables:**
|
|
48
|
+
Duplicate the config file in the backend.
|
|
49
|
+
```bash
|
|
50
|
+
cd ../backend
|
|
51
|
+
cp .env.example .env
|
|
52
|
+
```
|
|
53
|
+
Edit `.env` and fill in your `GEMINI_API_KEY` and a random string for `MEMORY_ENCRYPTION_KEY`.
|
|
54
|
+
|
|
55
|
+
3. **Build Frontend & Start:**
|
|
56
|
+
```bash
|
|
57
|
+
cd ../frontend
|
|
58
|
+
npm run build
|
|
59
|
+
cp -r dist/* ../backend/public/
|
|
60
|
+
cd ../backend
|
|
61
|
+
npm start # Or use `node bin/samarthya.js start` if you have it linked globally
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
4. **Access the Control Center:**
|
|
65
|
+
Open `http://localhost:5000` in your browser.
|
|
66
|
+
|
|
67
|
+
## ЁЯФМ Developing Plugins
|
|
68
|
+
|
|
69
|
+
Want to give your AI a new Superpower? Just make a simple JS file in `~/SamarthyaBot_Files/plugins/my_tool.js`:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
module.exports = {
|
|
73
|
+
name: 'greet_user',
|
|
74
|
+
description: 'Greets the user nicely',
|
|
75
|
+
riskLevel: 'low',
|
|
76
|
+
category: 'tool',
|
|
77
|
+
parameters: {
|
|
78
|
+
name: { type: 'string', required: true, description: 'User name' }
|
|
79
|
+
},
|
|
80
|
+
execute: async (args, userContext) => {
|
|
81
|
+
return { success: true, result: `Hello ${args.name}!` };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
Restart the server, and the agent will instantly know how to greet users using your custom logic!
|
|
86
|
+
|
|
87
|
+
## ЁЯФР Privacy Guarantee
|
|
88
|
+
|
|
89
|
+
All your conversations, file reads, and parsed memories remain completely local in your MongoDB database and `~/SamarthyaBot_Files/` directory. API data is only sent to the LLM Provider you specific (Gemini or Local Ollama).
|
|
90
|
+
|
|
91
|
+
## ЁЯУД License
|
|
92
|
+
MIT License
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
GEMINI_API_KEY=your_gemini_api_key_here
|
|
2
|
+
MONGO_URI=mongodb://localhost:27017/samarthya
|
|
3
|
+
PORT=5000
|
|
4
|
+
|
|
5
|
+
# Security (Required for Encrypted Vault)
|
|
6
|
+
MEMORY_ENCRYPTION_KEY=generate_a_random_32_character_string_here
|
|
7
|
+
|
|
8
|
+
# Optional Email Integration
|
|
9
|
+
# SMTP_EMAIL=your_email@gmail.com
|
|
10
|
+
# SMTP_PASSWORD=your_16_char_app_password
|
|
11
|
+
|
|
12
|
+
# Optional Desktop Application Features
|
|
13
|
+
USE_OLLAMA=false
|
|
14
|
+
OLLAMA_URL=http://localhost:11434
|
|
15
|
+
OLLAMA_MODEL=llama3.2
|
|
16
|
+
ACTIVE_PROVIDER=gemini
|
|
17
|
+
ACTIVE_MODEL=gemini-2.5-flash-lite
|
|
18
|
+
|
|
19
|
+
# Optional Telegram Remote Control
|
|
20
|
+
# TELEGRAM_BOT_TOKEN=your_telegram_bot_token
|
|
21
|
+
|
|
22
|
+
# Node Environment
|
|
23
|
+
NODE_ENV=production
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { execSync, spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const command = args[0];
|
|
9
|
+
|
|
10
|
+
const backendDir = path.join(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
// Helper to check if server is already running on port 5000
|
|
13
|
+
const isServerRunning = () => {
|
|
14
|
+
try {
|
|
15
|
+
execSync('fuser 5000/tcp 2>/dev/null');
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!command) {
|
|
23
|
+
console.log(`
|
|
24
|
+
ЁЯдЦ SamarthyaBot Local AI Agent
|
|
25
|
+
Usage:
|
|
26
|
+
samarthya onboard - Setup your local environment
|
|
27
|
+
samarthya model - Change your active AI provider/model
|
|
28
|
+
samarthya gateway - Start the local server
|
|
29
|
+
samarthya status - Check if the agent is running
|
|
30
|
+
samarthya tunnel - Expose to internet & setup webhooks
|
|
31
|
+
samarthya stop - Stop the running gateway
|
|
32
|
+
samarthya restart - Restart the gateway
|
|
33
|
+
`);
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
switch (command) {
|
|
38
|
+
case 'onboard':
|
|
39
|
+
console.log('ЁЯЪА Running SamarthyaBot Setup Wizard...');
|
|
40
|
+
|
|
41
|
+
const rl = readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const question = (query) => new Promise(resolve => rl.question(query, resolve));
|
|
47
|
+
|
|
48
|
+
(async () => {
|
|
49
|
+
console.log("\nЁЯМР Select your primary AI Provider:");
|
|
50
|
+
console.log(" 1) Google Gemini (Default)");
|
|
51
|
+
console.log(" 2) Anthropic Claude");
|
|
52
|
+
console.log(" 3) Groq (Fastest)");
|
|
53
|
+
console.log(" 4) OpenAI");
|
|
54
|
+
console.log(" 5) Local Ollama (Offline)");
|
|
55
|
+
console.log(" 6) Mistral AI\n");
|
|
56
|
+
|
|
57
|
+
let providerRaw = await question("Enter choice (1-6, default 1): ");
|
|
58
|
+
let activeProvider = 'gemini';
|
|
59
|
+
let useOllama = 'false';
|
|
60
|
+
|
|
61
|
+
switch (providerRaw.trim()) {
|
|
62
|
+
case '2': activeProvider = 'anthropic'; break;
|
|
63
|
+
case '3': activeProvider = 'groq'; break;
|
|
64
|
+
case '4': activeProvider = 'openai'; break;
|
|
65
|
+
case '5':
|
|
66
|
+
activeProvider = 'ollama';
|
|
67
|
+
useOllama = 'true';
|
|
68
|
+
break;
|
|
69
|
+
case '6': activeProvider = 'mistral'; break;
|
|
70
|
+
case '1':
|
|
71
|
+
default:
|
|
72
|
+
activeProvider = 'gemini';
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`\nЁЯСЙ Selected Provider: ${activeProvider.toUpperCase()}\n`);
|
|
77
|
+
|
|
78
|
+
const geminiKey = await question('ЁЯФС Enter Google Gemini API Key (or press Enter to skip if already set): ');
|
|
79
|
+
const anthropicKey = await question('ЁЯФС Enter Anthropic (Claude) API Key (or press Enter to skip): ');
|
|
80
|
+
const groqKey = await question('ЁЯФС Enter Groq API Key (or press Enter to skip): ');
|
|
81
|
+
const openAiKey = await question('ЁЯФС Enter OpenAI API Key (or press Enter to skip): ');
|
|
82
|
+
|
|
83
|
+
const envPath = path.join(backendDir, '.env');
|
|
84
|
+
let envVars = {};
|
|
85
|
+
|
|
86
|
+
// Read existing if present to keep other configs
|
|
87
|
+
if (fs.existsSync(envPath)) {
|
|
88
|
+
const currentEnv = fs.readFileSync(envPath, 'utf8');
|
|
89
|
+
currentEnv.split('\n').forEach(line => {
|
|
90
|
+
const [k, v] = line.split('=');
|
|
91
|
+
if (k && v) envVars[k.trim()] = v.trim();
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
envVars = {
|
|
95
|
+
PORT: '5000',
|
|
96
|
+
MONGODB_URI: 'mongodb://localhost:27017/samarthya',
|
|
97
|
+
JWT_SECRET: 'samarthya_secret_key_change_in_production',
|
|
98
|
+
NODE_ENV: 'production',
|
|
99
|
+
CORS_ORIGIN: 'http://localhost:5000',
|
|
100
|
+
USE_OLLAMA: useOllama,
|
|
101
|
+
ACTIVE_PROVIDER: activeProvider,
|
|
102
|
+
ACTIVE_MODEL: activeProvider === 'gemini' ? 'gemini-2.5-flash' : '',
|
|
103
|
+
OLLAMA_URL: 'http://localhost:11434',
|
|
104
|
+
OLLAMA_MODEL: 'dolphin3:8b-llama3.1-q4_K_M'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
envVars['USE_OLLAMA'] = useOllama;
|
|
109
|
+
envVars['ACTIVE_PROVIDER'] = activeProvider;
|
|
110
|
+
|
|
111
|
+
// Assign keys if provided
|
|
112
|
+
if (geminiKey.trim()) envVars['GEMINI_API_KEY'] = geminiKey.trim();
|
|
113
|
+
if (anthropicKey.trim()) envVars['ANTHROPIC_API_KEY'] = anthropicKey.trim();
|
|
114
|
+
if (groqKey.trim()) envVars['GROQ_API_KEY'] = groqKey.trim();
|
|
115
|
+
if (openAiKey.trim()) envVars['OPENAI_API_KEY'] = openAiKey.trim();
|
|
116
|
+
|
|
117
|
+
if (!envVars['GEMINI_API_KEY']) envVars['GEMINI_API_KEY'] = 'dummy';
|
|
118
|
+
|
|
119
|
+
// Write back to .env
|
|
120
|
+
const newEnvContent = Object.keys(envVars).map(k => `${k}=${envVars[k]}`).join('\n');
|
|
121
|
+
fs.writeFileSync(envPath, newEnvContent);
|
|
122
|
+
console.log('\nтЬЕ Keys saved to .env file securely.');
|
|
123
|
+
|
|
124
|
+
console.log('ЁЯУж Installing backend dependencies (this might take a few seconds)...');
|
|
125
|
+
try {
|
|
126
|
+
execSync('npm install --production', { cwd: backendDir, stdio: 'ignore' });
|
|
127
|
+
} catch (e) { /* ignore */ }
|
|
128
|
+
|
|
129
|
+
console.log('\nтЬи Onboarding complete! Run "samarthya gateway" to start your AI Operator.');
|
|
130
|
+
rl.close();
|
|
131
|
+
process.exit(0);
|
|
132
|
+
})();
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case 'model':
|
|
136
|
+
console.log('ЁЯФД Changing SamarthyaBot Active AI Model...');
|
|
137
|
+
const rlModel = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
138
|
+
const qModel = (query) => new Promise(res => rlModel.question(query, res));
|
|
139
|
+
|
|
140
|
+
(async () => {
|
|
141
|
+
console.log("\nЁЯМР Select your primary AI Provider:");
|
|
142
|
+
console.log(" 1) Google Gemini (Default)");
|
|
143
|
+
console.log(" 2) Anthropic Claude");
|
|
144
|
+
console.log(" 3) Groq (Fastest)");
|
|
145
|
+
console.log(" 4) OpenAI");
|
|
146
|
+
console.log(" 5) Local Ollama (Offline)");
|
|
147
|
+
console.log(" 6) Mistral AI\n");
|
|
148
|
+
|
|
149
|
+
let pRaw = await qModel("Enter choice (1-6, default 1): ");
|
|
150
|
+
let aProv = 'gemini';
|
|
151
|
+
let uOll = 'false';
|
|
152
|
+
|
|
153
|
+
switch (pRaw.trim()) {
|
|
154
|
+
case '2': aProv = 'anthropic'; break;
|
|
155
|
+
case '3': aProv = 'groq'; break;
|
|
156
|
+
case '4': aProv = 'openai'; break;
|
|
157
|
+
case '5': aProv = 'ollama'; uOll = 'true'; break;
|
|
158
|
+
case '6': aProv = 'mistral'; break;
|
|
159
|
+
default: aProv = 'gemini'; break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let aMod = '';
|
|
163
|
+
console.log(`\nЁЯУМ Sub-Models for ${aProv.toUpperCase()}:`);
|
|
164
|
+
if (aProv === 'gemini') {
|
|
165
|
+
console.log(" 1) gemini-2.5-flash (Fastest reasoning, great pricing)");
|
|
166
|
+
console.log(" 2) gemini-2.5-pro (Advanced complex multi-step reasoning)");
|
|
167
|
+
console.log(" 3) gemini-2.5-flash-lite (Fastest, budget friendly)");
|
|
168
|
+
console.log(" 4) gemini-2.0-flash (Older 1M context version)");
|
|
169
|
+
const mSel = await qModel("Enter model choice (or type custom model ID directly): ");
|
|
170
|
+
if (mSel.trim() === '1' || mSel.trim() === '') aMod = 'gemini-2.5-flash';
|
|
171
|
+
else if (mSel.trim() === '2') aMod = 'gemini-2.5-pro';
|
|
172
|
+
else if (mSel.trim() === '3') aMod = 'gemini-2.5-flash-lite';
|
|
173
|
+
else if (mSel.trim() === '4') aMod = 'gemini-2.0-flash';
|
|
174
|
+
else aMod = mSel.trim();
|
|
175
|
+
} else if (aProv === 'openai') {
|
|
176
|
+
console.log(" 1) gpt-5.2 (Best for coding & agentic tasks)");
|
|
177
|
+
console.log(" 2) gpt-5-mini (Faster, cost-efficient GPT-5)");
|
|
178
|
+
console.log(" 3) gpt-4o (Fast, intelligent, flexible)");
|
|
179
|
+
console.log(" 4) o3-mini (Small reasoning alternative)");
|
|
180
|
+
const mSel = await qModel("Enter model choice (or type custom ID): ");
|
|
181
|
+
if (mSel.trim() === '1') aMod = 'gpt-5.2';
|
|
182
|
+
else if (mSel.trim() === '2') aMod = 'gpt-5-mini';
|
|
183
|
+
else if (mSel.trim() === '3') aMod = 'gpt-4o';
|
|
184
|
+
else if (mSel.trim() === '4') aMod = 'o3-mini';
|
|
185
|
+
else aMod = mSel.trim();
|
|
186
|
+
} else if (aProv === 'groq') {
|
|
187
|
+
console.log(" 1) llama-3.3-70b-versatile (Best overall Llama)");
|
|
188
|
+
console.log(" 2) llama-3.1-8b-instant (Extreme Fast Llama)");
|
|
189
|
+
console.log(" 3) qwen/qwen3-32b (Powerful 32b reasoning)");
|
|
190
|
+
const mSel = await qModel("Enter model choice (or type custom ID): ");
|
|
191
|
+
if (mSel.trim() === '1' || mSel.trim() === '') aMod = 'llama-3.3-70b-versatile';
|
|
192
|
+
else if (mSel.trim() === '2') aMod = 'llama-3.1-8b-instant';
|
|
193
|
+
else if (mSel.trim() === '3') aMod = 'qwen/qwen3-32b';
|
|
194
|
+
else aMod = mSel.trim();
|
|
195
|
+
} else if (aProv === 'mistral') {
|
|
196
|
+
console.log(" 1) mistral-large-3 (General-purpose multimodal)");
|
|
197
|
+
console.log(" 2) ministral-3-8b (Powerful & efficient)");
|
|
198
|
+
console.log(" 3) mistral-small-3.2 (Small latest model)");
|
|
199
|
+
console.log(" 4) devstral-2 (Best for code & agents)");
|
|
200
|
+
const mSel = await qModel("Enter model choice (or type custom ID): ");
|
|
201
|
+
if (mSel.trim() === '1' || mSel.trim() === '') aMod = 'mistral-large-3';
|
|
202
|
+
else if (mSel.trim() === '2') aMod = 'ministral-3-8b';
|
|
203
|
+
else if (mSel.trim() === '3') aMod = 'mistral-small-3.2';
|
|
204
|
+
else if (mSel.trim() === '4') aMod = 'devstral-2';
|
|
205
|
+
else aMod = mSel.trim();
|
|
206
|
+
} else if (aProv === 'anthropic') {
|
|
207
|
+
console.log(" 1) claude-3-5-sonnet-latest");
|
|
208
|
+
console.log(" 2) claude-3-opus-latest");
|
|
209
|
+
const mSel = await qModel("Enter model choice (or type custom ID): ");
|
|
210
|
+
if (mSel.trim() === '1' || mSel.trim() === '') aMod = 'claude-3-5-sonnet-latest';
|
|
211
|
+
else if (mSel.trim() === '2') aMod = 'claude-3-opus-latest';
|
|
212
|
+
else aMod = mSel.trim();
|
|
213
|
+
} else if (aProv === 'ollama') {
|
|
214
|
+
console.log(" Your models list usually includes:");
|
|
215
|
+
console.log(" 1) dolphin3:8b-llama3.1-q4_K_M (Default)");
|
|
216
|
+
console.log(" 2) llama3:8b");
|
|
217
|
+
console.log(" 3) mistral");
|
|
218
|
+
const mSel = await qModel("Enter model choice (or type custom Ollama tag): ");
|
|
219
|
+
if (mSel.trim() === '1' || mSel.trim() === '') aMod = 'dolphin3:8b-llama3.1-q4_K_M';
|
|
220
|
+
else if (mSel.trim() === '2') aMod = 'llama3:8b';
|
|
221
|
+
else if (mSel.trim() === '3') aMod = 'mistral';
|
|
222
|
+
else aMod = mSel.trim();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const ePath = path.join(backendDir, '.env');
|
|
226
|
+
if (fs.existsSync(ePath)) {
|
|
227
|
+
let currentEnv = fs.readFileSync(ePath, 'utf8');
|
|
228
|
+
let lines = currentEnv.split('\n');
|
|
229
|
+
let updated = false;
|
|
230
|
+
let mUpdated = false;
|
|
231
|
+
|
|
232
|
+
lines = lines.map(line => {
|
|
233
|
+
if (line.startsWith('ACTIVE_PROVIDER=')) { updated = true; return `ACTIVE_PROVIDER=${aProv}`; }
|
|
234
|
+
if (line.startsWith('ACTIVE_MODEL=')) { mUpdated = true; return `ACTIVE_MODEL=${aMod}`; }
|
|
235
|
+
if (line.startsWith('USE_OLLAMA=')) { uUpdated = true; return `USE_OLLAMA=${uOll}`; }
|
|
236
|
+
if (aProv === 'ollama' && line.startsWith('OLLAMA_MODEL=')) { return `OLLAMA_MODEL=${aMod}`; }
|
|
237
|
+
return line;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
if (!updated) lines.push(`ACTIVE_PROVIDER=${aProv}`);
|
|
241
|
+
if (!mUpdated) lines.push(`ACTIVE_MODEL=${aMod}`);
|
|
242
|
+
if (!uUpdated) lines.push(`USE_OLLAMA=${uOll}`);
|
|
243
|
+
|
|
244
|
+
fs.writeFileSync(ePath, lines.join('\n'));
|
|
245
|
+
console.log(`\nтЬЕ Model successfully switched to: ${aProv.toUpperCase()}!`);
|
|
246
|
+
console.log('ЁЯФД Please restart the gateway if it is currently running.');
|
|
247
|
+
} else {
|
|
248
|
+
console.log('тЭМ Error: .env file not found. Please run "samarthya onboard" first.');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
rlModel.close();
|
|
252
|
+
})();
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case 'gateway':
|
|
256
|
+
if (isServerRunning()) {
|
|
257
|
+
console.log('тЪая╕П Gateway is already running on port 5000!');
|
|
258
|
+
console.log('ЁЯМР Access the dashboard at http://localhost:5000');
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try { require('dotenv').config({ path: path.join(backendDir, '.env') }); } catch (e) { /* ignore if not installed */ }
|
|
263
|
+
|
|
264
|
+
const activeProvider = process.env.ACTIVE_PROVIDER ? process.env.ACTIVE_PROVIDER.toUpperCase() : 'GEMINI';
|
|
265
|
+
const activeModel = process.env.ACTIVE_MODEL || 'gemini-2.5-flash';
|
|
266
|
+
|
|
267
|
+
console.log('ЁЯЪА Starting SamarthyaBot Gateway in the background...');
|
|
268
|
+
console.log(`ЁЯза Local Config: Using ${activeProvider} (${activeModel})`);
|
|
269
|
+
console.log('ЁЯТб Tip: Run "samarthya model" to change your AI provider/model at any time.');
|
|
270
|
+
console.log('ЁЯМР You can access your agent dashboard at http://localhost:5000\n');
|
|
271
|
+
|
|
272
|
+
// We use spawn to run the server
|
|
273
|
+
const child = spawn('node', ['server.js'], {
|
|
274
|
+
cwd: backendDir,
|
|
275
|
+
stdio: 'inherit'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
child.on('close', (code) => {
|
|
279
|
+
console.log(`Gateway exited with code ${code}`);
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
|
|
283
|
+
case 'status':
|
|
284
|
+
if (isServerRunning()) {
|
|
285
|
+
console.log('ЁЯЯв SamarthyaBot Gateway is actively running on port 5000.');
|
|
286
|
+
console.log('ЁЯМР Dashboard: http://localhost:5000');
|
|
287
|
+
} else {
|
|
288
|
+
console.log('ЁЯФ┤ SamarthyaBot Gateway is offline. Run "samarthya gateway" to start.');
|
|
289
|
+
}
|
|
290
|
+
break;
|
|
291
|
+
|
|
292
|
+
case 'tunnel':
|
|
293
|
+
console.log('ЁЯЪЗ Starting LocalTunnel to expose port 5000 to the internet...');
|
|
294
|
+
if (!isServerRunning()) {
|
|
295
|
+
console.log('тЪая╕П Warning: SamarthyaBot Gateway is not running! Run "samarthya gateway" in another terminal first.');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try { require('dotenv').config({ path: path.join(backendDir, '.env') }); } catch (e) { }
|
|
299
|
+
|
|
300
|
+
const tunnelProcess = spawn('npx', ['localtunnel', '--port', '5000'], { stdio: 'pipe' });
|
|
301
|
+
|
|
302
|
+
tunnelProcess.stdout.on('data', async (data) => {
|
|
303
|
+
const output = data.toString();
|
|
304
|
+
console.log(output.trim());
|
|
305
|
+
const match = output.match(/your url is: (https:\/\/.+)/);
|
|
306
|
+
if (match && match[1]) {
|
|
307
|
+
const publicUrl = match[1];
|
|
308
|
+
console.log(`\nтЬЕ Public Gateway URL: ${publicUrl}`);
|
|
309
|
+
|
|
310
|
+
// Set Telegram Webhook Automatically
|
|
311
|
+
if (process.env.TELEGRAM_BOT_TOKEN) {
|
|
312
|
+
console.log('ЁЯФЧ Setting Telegram Webhook...');
|
|
313
|
+
try {
|
|
314
|
+
const tgUrl = `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/setWebhook?url=${publicUrl}/api/telegram/webhook`;
|
|
315
|
+
const res = await fetch(tgUrl);
|
|
316
|
+
const result = await res.json();
|
|
317
|
+
if (result.ok) {
|
|
318
|
+
console.log('ЁЯЯв Telegram Webhook Set Successfully!');
|
|
319
|
+
} else {
|
|
320
|
+
console.log('ЁЯФ┤ Failed to set Telegram Webhook:', result.description);
|
|
321
|
+
}
|
|
322
|
+
} catch (err) {
|
|
323
|
+
console.log('ЁЯФ┤ Error setting Telegram Webhook:', err.message);
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
console.log('тД╣я╕П TELEGRAM_BOT_TOKEN not found in .env. Skipping Telegram Webhook setup.');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
console.log('\nЁЯУ▒ Put this URL in your Meta WhatsApp App Dashboard:');
|
|
330
|
+
console.log(` ${publicUrl}/api/whatsapp/webhook`);
|
|
331
|
+
console.log('\n(Leave this terminal running to keep the tunnel open natively)');
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
tunnelProcess.stderr.on('data', (data) => {
|
|
336
|
+
console.error('Tunnel Error:', data.toString());
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
tunnelProcess.on('close', (code) => {
|
|
340
|
+
console.log(`Tunnel closed with code ${code}`);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
break;
|
|
344
|
+
|
|
345
|
+
case 'stop':
|
|
346
|
+
if (isServerRunning()) {
|
|
347
|
+
console.log('ЁЯЫС Stopping SamarthyaBot Gateway...');
|
|
348
|
+
try {
|
|
349
|
+
execSync('fuser -k 5000/tcp 2>/dev/null');
|
|
350
|
+
console.log('тЬЕ Gateway stopped successfully.');
|
|
351
|
+
} catch (e) {
|
|
352
|
+
console.log('тЭМ Failed to stop gateway gracefully. Process might already be dead.');
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
console.log('тЪая╕П Gateway is not currently running.');
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
|
|
359
|
+
case 'restart':
|
|
360
|
+
console.log('ЁЯФД Restarting SamarthyaBot Gateway...');
|
|
361
|
+
if (isServerRunning()) {
|
|
362
|
+
try {
|
|
363
|
+
execSync('fuser -k 5000/tcp 2>/dev/null');
|
|
364
|
+
} catch (e) { /* ignore */ }
|
|
365
|
+
}
|
|
366
|
+
// Give it a moment to free the port
|
|
367
|
+
setTimeout(() => {
|
|
368
|
+
const restartChild = spawn('node', ['server.js'], {
|
|
369
|
+
cwd: backendDir,
|
|
370
|
+
stdio: 'inherit'
|
|
371
|
+
});
|
|
372
|
+
restartChild.on('close', (code) => {
|
|
373
|
+
console.log(`Gateway exited with code ${code}`);
|
|
374
|
+
});
|
|
375
|
+
console.log('тЬЕ Gateway restarted successfully!');
|
|
376
|
+
console.log('ЁЯМР Dashboard: http://localhost:5000\n');
|
|
377
|
+
}, 1000);
|
|
378
|
+
break;
|
|
379
|
+
|
|
380
|
+
default:
|
|
381
|
+
console.log(`тЭМ Unknown command: ${command}`);
|
|
382
|
+
console.log('Try "samarthya onboard" or "samarthya gateway"');
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
SUPPORTED_LANGUAGES: ['hindi', 'hinglish', 'english'],
|
|
3
|
+
|
|
4
|
+
TOOL_PACKS: {
|
|
5
|
+
student: {
|
|
6
|
+
name: 'ЁЯОУ Student Pack',
|
|
7
|
+
nameHi: 'ЁЯОУ рд╕реНрдЯреВрдбреЗрдВрдЯ рдкреИрдХ',
|
|
8
|
+
tools: ['web_search', 'summarize_text', 'note_take', 'calculate', 'reminder_set', 'file_read', 'file_write', 'file_list', 'weather_info', 'run_command', 'capture_desktop_screenshot', 'schedule_background_task', 'simulate_task'],
|
|
9
|
+
description: 'Assignment summary, notes, exam reminders, calculations'
|
|
10
|
+
},
|
|
11
|
+
business: {
|
|
12
|
+
name: 'ЁЯПв Small Business Pack',
|
|
13
|
+
nameHi: 'ЁЯПв рдмрд┐рдЬрд╝рдиреЗрд╕ рдкреИрдХ',
|
|
14
|
+
tools: ['web_search', 'send_email', 'calendar_schedule', 'gst_reminder', 'calculate', 'file_read', 'file_write', 'file_list', 'upi_generate', 'note_take', 'reminder_set', 'weather_info', 'system_info', 'run_command', 'capture_desktop_screenshot', 'schedule_background_task', 'simulate_task'],
|
|
15
|
+
description: 'GST reminders, email, UPI links, file management'
|
|
16
|
+
},
|
|
17
|
+
developer: {
|
|
18
|
+
name: 'ЁЯСитАНЁЯТ╗ Developer Pack',
|
|
19
|
+
nameHi: 'ЁЯСитАНЁЯТ╗ рдбреЗрд╡рд▓рдкрд░ рдкреИрдХ',
|
|
20
|
+
tools: ['web_search', 'file_read', 'file_write', 'file_list', 'calculate', 'send_email', 'run_command', 'system_info', 'open_url', 'note_take', 'reminder_set', 'capture_desktop_screenshot', 'schedule_background_task', 'simulate_task'],
|
|
21
|
+
description: 'Shell commands, file ops, system info, browser control'
|
|
22
|
+
},
|
|
23
|
+
personal: {
|
|
24
|
+
name: 'ЁЯПа Personal Pack',
|
|
25
|
+
nameHi: 'ЁЯПа рдкрд░реНрд╕рдирд▓ рдкреИрдХ',
|
|
26
|
+
tools: ['web_search', 'reminder_set', 'note_take', 'calculate', 'weather_info', 'file_read', 'file_write', 'file_list', 'calendar_schedule', 'upi_generate', 'send_email', 'system_info', 'open_url', 'run_command', 'capture_desktop_screenshot', 'schedule_background_task', 'simulate_task'],
|
|
27
|
+
description: 'Weather, notes, reminders, UPI, email, files'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
PERMISSION_LEVELS: {
|
|
32
|
+
ALLOW_ONCE: 'allow_once',
|
|
33
|
+
ALLOW_ALWAYS: 'allow_always',
|
|
34
|
+
NEVER: 'never',
|
|
35
|
+
ASK: 'ask'
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
SENSITIVE_PATTERNS: {
|
|
39
|
+
PAN: /[A-Z]{5}[0-9]{4}[A-Z]/g,
|
|
40
|
+
AADHAAR: /\b\d{4}\s?\d{4}\s?\d{4}\b/g,
|
|
41
|
+
PHONE: /(\+91|0)?[6-9]\d{9}/g,
|
|
42
|
+
EMAIL: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
43
|
+
BANK_ACCOUNT: /\d{9,18}/g,
|
|
44
|
+
IFSC: /[A-Z]{4}0[A-Z0-9]{6}/g
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
INDIAN_HOLIDAYS_2025: [
|
|
48
|
+
{ date: '2025-01-14', name: 'Makar Sankranti', nameHi: 'рдордХрд░ рд╕рдВрдХреНрд░рд╛рдВрддрд┐' },
|
|
49
|
+
{ date: '2025-01-26', name: 'Republic Day', nameHi: 'рдЧрдгрддрдВрддреНрд░ рджрд┐рд╡рд╕' },
|
|
50
|
+
{ date: '2025-03-14', name: 'Holi', nameHi: 'рд╣реЛрд▓реА' },
|
|
51
|
+
{ date: '2025-03-31', name: 'Eid ul-Fitr', nameHi: 'рдИрдж рдЙрд▓-рдлрд╝рд┐рддреНрд░' },
|
|
52
|
+
{ date: '2025-04-06', name: 'Ram Navami', nameHi: 'рд░рд╛рдо рдирд╡рдореА' },
|
|
53
|
+
{ date: '2025-04-14', name: 'Ambedkar Jayanti', nameHi: 'рдЕрдВрдмреЗрдбрдХрд░ рдЬрдпрдВрддреА' },
|
|
54
|
+
{ date: '2025-04-18', name: 'Good Friday', nameHi: 'рдЧреБрдб рдлреНрд░рд╛рдЗрдбреЗ' },
|
|
55
|
+
{ date: '2025-05-01', name: 'May Day', nameHi: 'рдордЬрд╝рджреВрд░ рджрд┐рд╡рд╕' },
|
|
56
|
+
{ date: '2025-08-15', name: 'Independence Day', nameHi: 'рд╕реНрд╡рддрдВрддреНрд░рддрд╛ рджрд┐рд╡рд╕' },
|
|
57
|
+
{ date: '2025-08-27', name: 'Janmashtami', nameHi: 'рдЬрдиреНрдорд╛рд╖реНрдЯрдореА' },
|
|
58
|
+
{ date: '2025-10-02', name: 'Gandhi Jayanti', nameHi: 'рдЧрд╛рдВрдзреА рдЬрдпрдВрддреА' },
|
|
59
|
+
{ date: '2025-10-20', name: 'Dussehra', nameHi: 'рджрд╢рд╣рд░рд╛' },
|
|
60
|
+
{ date: '2025-11-01', name: 'Diwali', nameHi: 'рджреАрдкрд╛рд╡рд▓реА' },
|
|
61
|
+
{ date: '2025-11-05', name: 'Guru Nanak Jayanti', nameHi: 'рдЧреБрд░реБ рдирд╛рдирдХ рдЬрдпрдВрддреА' },
|
|
62
|
+
{ date: '2025-12-25', name: 'Christmas', nameHi: 'рдХреНрд░рд┐рд╕рдорд╕' }
|
|
63
|
+
],
|
|
64
|
+
|
|
65
|
+
RISK_LEVELS: {
|
|
66
|
+
LOW: 'low',
|
|
67
|
+
MEDIUM: 'medium',
|
|
68
|
+
HIGH: 'high',
|
|
69
|
+
CRITICAL: 'critical'
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const connectDB = async () => {
|
|
4
|
+
try {
|
|
5
|
+
const conn = await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/samarthya');
|
|
6
|
+
console.log(`тЬЕ MongoDB Connected: ${conn.connection.host}`);
|
|
7
|
+
} catch (error) {
|
|
8
|
+
console.error(`тЭМ MongoDB Error: ${error.message}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = connectDB;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const AuditLog = require('../models/AuditLog');
|
|
2
|
+
|
|
3
|
+
// Get audit logs with pagination
|
|
4
|
+
exports.getAuditLogs = async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const { page = 1, limit = 50, category, status } = req.query;
|
|
7
|
+
const query = {};
|
|
8
|
+
|
|
9
|
+
if (category) query.category = category;
|
|
10
|
+
if (status) query.status = status;
|
|
11
|
+
|
|
12
|
+
const logs = await AuditLog.find(query)
|
|
13
|
+
.sort({ createdAt: -1 })
|
|
14
|
+
.skip((page - 1) * limit)
|
|
15
|
+
.limit(parseInt(limit));
|
|
16
|
+
|
|
17
|
+
const total = await AuditLog.countDocuments(query);
|
|
18
|
+
|
|
19
|
+
res.json({
|
|
20
|
+
success: true,
|
|
21
|
+
logs,
|
|
22
|
+
pagination: {
|
|
23
|
+
page: parseInt(page),
|
|
24
|
+
limit: parseInt(limit),
|
|
25
|
+
total,
|
|
26
|
+
pages: Math.ceil(total / limit)
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
res.status(500).json({ success: false, message: error.message });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Get audit stats
|
|
35
|
+
exports.getAuditStats = async (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const userId = req.user.id;
|
|
38
|
+
|
|
39
|
+
const [totalActions, byCategory, byStatus, recentRisks] = await Promise.all([
|
|
40
|
+
AuditLog.countDocuments({}),
|
|
41
|
+
AuditLog.aggregate([
|
|
42
|
+
{ $group: { _id: '$category', count: { $sum: 1 } } }
|
|
43
|
+
]),
|
|
44
|
+
AuditLog.aggregate([
|
|
45
|
+
{ $group: { _id: '$status', count: { $sum: 1 } } }
|
|
46
|
+
]),
|
|
47
|
+
AuditLog.find({
|
|
48
|
+
'details.riskLevel': { $in: ['high', 'critical'] }
|
|
49
|
+
}).sort({ createdAt: -1 }).limit(10)
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
res.json({
|
|
53
|
+
success: true,
|
|
54
|
+
stats: {
|
|
55
|
+
totalActions,
|
|
56
|
+
byCategory: Object.fromEntries(byCategory.map(c => [c._id, c.count])),
|
|
57
|
+
byStatus: Object.fromEntries(byStatus.map(s => [s._id, s.count])),
|
|
58
|
+
recentHighRisks: recentRisks
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
res.status(500).json({ success: false, message: error.message });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Rollback an action
|
|
67
|
+
exports.rollbackAction = async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const log = await AuditLog.findOne({
|
|
70
|
+
_id: req.params.id,
|
|
71
|
+
canRollback: true
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!log) {
|
|
75
|
+
return res.status(404).json({ success: false, message: 'Action not found or cannot be rolled back' });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Mark as rolled back
|
|
79
|
+
log.status = 'rolled_back';
|
|
80
|
+
await log.save();
|
|
81
|
+
|
|
82
|
+
res.json({ success: true, message: 'Action rolled back successfully' });
|
|
83
|
+
} catch (error) {
|
|
84
|
+
res.status(500).json({ success: false, message: error.message });
|
|
85
|
+
}
|
|
86
|
+
};
|