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.
Files changed (50) hide show
  1. package/README.md +92 -0
  2. package/backend/.env.example +23 -0
  3. package/backend/bin/samarthya.js +384 -0
  4. package/backend/config/constants.js +71 -0
  5. package/backend/config/db.js +13 -0
  6. package/backend/controllers/auditController.js +86 -0
  7. package/backend/controllers/authController.js +154 -0
  8. package/backend/controllers/chatController.js +158 -0
  9. package/backend/controllers/fileController.js +268 -0
  10. package/backend/controllers/platformController.js +54 -0
  11. package/backend/controllers/screenController.js +91 -0
  12. package/backend/controllers/telegramController.js +120 -0
  13. package/backend/controllers/toolsController.js +56 -0
  14. package/backend/controllers/whatsappController.js +214 -0
  15. package/backend/fix_toolRegistry.js +25 -0
  16. package/backend/middleware/auth.js +28 -0
  17. package/backend/models/AuditLog.js +28 -0
  18. package/backend/models/BackgroundJob.js +13 -0
  19. package/backend/models/Conversation.js +40 -0
  20. package/backend/models/Memory.js +17 -0
  21. package/backend/models/User.js +24 -0
  22. package/backend/package-lock.json +3766 -0
  23. package/backend/package.json +41 -0
  24. package/backend/public/assets/index-Ckf0GO1B.css +1 -0
  25. package/backend/public/assets/index-Do4jNsZS.js +19 -0
  26. package/backend/public/assets/index-Ui-pyZvK.js +25 -0
  27. package/backend/public/favicon.svg +17 -0
  28. package/backend/public/index.html +18 -0
  29. package/backend/public/manifest.json +16 -0
  30. package/backend/routes/audit.js +9 -0
  31. package/backend/routes/auth.js +11 -0
  32. package/backend/routes/chat.js +11 -0
  33. package/backend/routes/files.js +14 -0
  34. package/backend/routes/platform.js +18 -0
  35. package/backend/routes/screen.js +10 -0
  36. package/backend/routes/telegram.js +8 -0
  37. package/backend/routes/tools.js +9 -0
  38. package/backend/routes/whatsapp.js +11 -0
  39. package/backend/server.js +134 -0
  40. package/backend/services/background/backgroundService.js +81 -0
  41. package/backend/services/llm/llmService.js +444 -0
  42. package/backend/services/memory/memoryService.js +159 -0
  43. package/backend/services/planner/plannerService.js +182 -0
  44. package/backend/services/security/securityService.js +166 -0
  45. package/backend/services/telegram/telegramService.js +49 -0
  46. package/backend/services/tools/toolRegistry.js +879 -0
  47. package/backend/services/whatsapp/whatsappService.js +254 -0
  48. package/backend/test_email.js +29 -0
  49. package/backend/test_parser.js +10 -0
  50. 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
+ };