samarthya-bot 2.2.0 → 2.3.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +340 -353
  3. package/backend/bin/samarthya.js +29 -10
  4. package/backend/config/constants.js +4 -4
  5. package/backend/controllers/chatController.js +72 -0
  6. package/backend/controllers/telegramController.js +8 -0
  7. package/backend/package-lock.json +2 -2
  8. package/backend/package.json +2 -2
  9. package/backend/public/assets/index-DiMx9ERJ.css +1 -0
  10. package/backend/public/assets/index-Dn5WYZTH.js +32 -0
  11. package/backend/public/index.html +186 -16
  12. package/backend/public/robots.txt +32 -0
  13. package/backend/public/sitemap.xml +39 -0
  14. package/backend/services/agent/commandService.js +168 -0
  15. package/backend/services/llm/llmService.js +8 -0
  16. package/backend/services/planner/plannerService.js +17 -0
  17. package/backend/services/security/sandboxService.js +11 -4
  18. package/backend/services/system/platform.js +150 -0
  19. package/backend/services/tools/toolRegistry.js +523 -37
  20. package/backend/services/worker/workerClient.js +141 -29
  21. package/package.json +2 -2
  22. package/backend/public/assets/index-6PCzI3K2.js +0 -40
  23. package/backend/public/assets/index-6TF5jVRQ.js +0 -149
  24. package/backend/public/assets/index-B0U7rt6f.js +0 -46
  25. package/backend/public/assets/index-BF0RZh9i.js +0 -149
  26. package/backend/public/assets/index-BFRAq8Y1.js +0 -149
  27. package/backend/public/assets/index-CGw8cc8z.js +0 -149
  28. package/backend/public/assets/index-Ckf0GO1B.css +0 -1
  29. package/backend/public/assets/index-Cx0Ei-z7.js +0 -149
  30. package/backend/public/assets/index-DIPdcLv-.js +0 -25
  31. package/backend/public/assets/index-Da1E-MYB.js +0 -53
  32. package/backend/public/assets/index-DdCKkq38.js +0 -149
  33. package/backend/public/assets/index-Do4jNsZS.js +0 -19
  34. package/backend/public/assets/index-DyjpBYmS.js +0 -51
  35. package/backend/public/assets/index-DzlXcaXT.js +0 -149
  36. package/backend/public/assets/index-J7XSVHCz.css +0 -1
  37. package/backend/public/assets/index-Ui-pyZvK.js +0 -25
  38. package/backend/public/assets/index-kzffNwzo.js +0 -149
@@ -1,42 +1,95 @@
1
1
  const { spawn } = require('child_process');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const fs = require('fs');
4
5
  const { v4: uuidv4 } = require('uuid');
5
-
6
+ const platform = require('../system/platform');
7
+
8
+ /**
9
+ * WorkerClient
10
+ * ------------------------------------------------------------------
11
+ * Streams long-running shell commands back to Node.
12
+ *
13
+ * Fast path : the pre-built Go micro-worker (`worker/samarthya-worker`).
14
+ * Fallback : a pure-Node executor using the platform shell.
15
+ *
16
+ * The Go binary is only shipped for one architecture, so on Windows /
17
+ * macOS / unsupported CPUs we transparently fall back to Node. This is
18
+ * what makes `devops_execute_stream` work on every OS.
19
+ */
6
20
  class WorkerClient {
7
21
  constructor() {
8
22
  this.workerProcess = null;
9
23
  this.pendingRequests = new Map();
24
+ this.useNativeFallback = false; // flipped on when the Go binary can't run
25
+ this.startAttempts = 0;
10
26
 
11
- // Find the worker binary.
12
- // In dev, it's inside `worker/samarthya-worker`
13
- // In prod (npm module), it might be inside the module root.
27
+ const projectRoot = path.resolve(__dirname, '../../../');
28
+ const binName = 'samarthya-worker' + (platform.isWindows ? '.exe' : '');
29
+ this.workerPath = path.join(projectRoot, 'worker', binName);
30
+ }
14
31
 
15
- // Let's resolve it more robustly:
16
- const projectRoot = path.resolve(__dirname, '../../../'); // Gives /home/bishnups/Documents/PROJECT_DEB
17
- this.workerPath = path.join(projectRoot, 'worker', 'samarthya-worker' + (os.platform() === 'win32' ? '.exe' : ''));
32
+ /** Whether the Go binary exists for THIS platform. */
33
+ _binaryAvailable() {
34
+ try {
35
+ return fs.existsSync(this.workerPath);
36
+ } catch {
37
+ return false;
38
+ }
18
39
  }
19
40
 
20
41
  start() {
21
- if (this.workerProcess) return;
42
+ if (this.workerProcess || this.useNativeFallback) return;
43
+
44
+ if (!this._binaryAvailable()) {
45
+ console.log('[Worker] Go micro-worker binary not found for this platform — using native Node executor.');
46
+ this.useNativeFallback = true;
47
+ return;
48
+ }
49
+
50
+ // Avoid infinite respawn loops if the binary exists but cannot run
51
+ // (e.g. wrong CPU architecture). After 3 failed starts, fall back.
52
+ if (this.startAttempts >= 3) {
53
+ console.log('[Worker] Go worker failed repeatedly — switching to native Node executor.');
54
+ this.useNativeFallback = true;
55
+ return;
56
+ }
57
+ this.startAttempts++;
22
58
 
23
59
  console.log(`[Worker] Starting Go Micro-Worker from: ${this.workerPath}`);
24
60
 
25
- this.workerProcess = spawn(this.workerPath, [], {
26
- stdio: ['pipe', 'pipe', 'inherit'] // We write to stdin, read from stdout
27
- });
61
+ try {
62
+ this.workerProcess = spawn(this.workerPath, [], {
63
+ stdio: ['pipe', 'pipe', 'inherit'],
64
+ });
65
+ } catch (err) {
66
+ console.error('[Worker] Could not spawn Go worker:', err.message);
67
+ this.useNativeFallback = true;
68
+ return;
69
+ }
28
70
 
29
71
  this.workerProcess.on('error', (err) => {
30
72
  console.error('[Worker] Failed to start Go worker:', err.message);
31
- console.error('[Worker] Make sure you have run `cd worker && go build -o samarthya-worker`');
73
+ // Binary is present but not runnable on this host go native.
74
+ this.useNativeFallback = true;
75
+ this.workerProcess = null;
32
76
  });
33
77
 
34
78
  this.workerProcess.on('exit', (code) => {
35
- console.log(`[Worker] Go worker exited with code ${code}. Restarting in 5s...`);
36
79
  this.workerProcess = null;
80
+ if (this.useNativeFallback) return;
81
+ if (this.startAttempts >= 3) {
82
+ this.useNativeFallback = true;
83
+ console.log('[Worker] Go worker keeps exiting — switching to native Node executor.');
84
+ return;
85
+ }
86
+ console.log(`[Worker] Go worker exited with code ${code}. Restarting in 5s...`);
37
87
  setTimeout(() => this.start(), 5000);
38
88
  });
39
89
 
90
+ // Reset attempt counter once we have stayed up for a while.
91
+ setTimeout(() => { if (this.workerProcess) this.startAttempts = 0; }, 10000);
92
+
40
93
  // Listen to JSON stream line by line
41
94
  let buffer = '';
42
95
  this.workerProcess.stdout.on('data', (chunk) => {
@@ -60,7 +113,6 @@ class WorkerClient {
60
113
 
61
114
  _handleResponse(res) {
62
115
  if (!res.id || !this.pendingRequests.has(res.id)) {
63
- // Unbound message or logging
64
116
  if (res.type === 'error') {
65
117
  console.error(`[Worker Msg] ${res.data}`);
66
118
  }
@@ -73,7 +125,6 @@ class WorkerClient {
73
125
  if (handlers.onStream) {
74
126
  handlers.onStream(res.data, res.type);
75
127
  } else {
76
- // If the user didn't provide a stream handler, buffer it internally
77
128
  if (!handlers.buffer) handlers.buffer = '';
78
129
  handlers.buffer += res.data + '\n';
79
130
  }
@@ -84,7 +135,7 @@ class WorkerClient {
84
135
  success: res.exitCode === 0,
85
136
  exitCode: res.exitCode,
86
137
  output: finalData,
87
- elapsed: res.elapsedTimeMs
138
+ elapsed: res.elapsedTimeMs,
88
139
  });
89
140
  this.pendingRequests.delete(res.id);
90
141
  }
@@ -93,40 +144,102 @@ class WorkerClient {
93
144
  success: false,
94
145
  exitCode: -1,
95
146
  output: res.data,
96
- elapsed: 0
147
+ elapsed: 0,
97
148
  });
98
149
  this.pendingRequests.delete(res.id);
99
150
  }
100
151
  }
101
152
 
153
+ /**
154
+ * Native pure-Node fallback executor. Runs the command through the
155
+ * platform shell and streams stdout/stderr back via the callback,
156
+ * resolving with the same shape the Go worker produces.
157
+ */
158
+ _executeNative(command, dir = '', streamCallback = null) {
159
+ return new Promise((resolve) => {
160
+ const { shell, flag } = platform.getShell();
161
+ const flagArgs = flag.split(' ');
162
+ const startTime = Date.now();
163
+ let output = '';
164
+
165
+ let child;
166
+ try {
167
+ child = spawn(shell, [...flagArgs, command], {
168
+ cwd: dir && fs.existsSync(dir) ? dir : process.cwd(),
169
+ env: process.env,
170
+ windowsHide: true,
171
+ });
172
+ } catch (err) {
173
+ return resolve({ success: false, exitCode: -1, output: `Spawn error: ${err.message}`, elapsed: 0 });
174
+ }
175
+
176
+ // Hard timeout so a hung command can't wedge the agent forever.
177
+ const timeout = setTimeout(() => {
178
+ try { child.kill('SIGKILL'); } catch (_) { }
179
+ output += '\n[timed out after 5 minutes]';
180
+ }, 5 * 60 * 1000);
181
+
182
+ const onData = (data, type) => {
183
+ const text = data.toString();
184
+ output += text;
185
+ if (output.length > 1024 * 1024) output = output.slice(-1024 * 1024); // cap 1MB
186
+ if (streamCallback) streamCallback(text, type);
187
+ };
188
+
189
+ child.stdout.on('data', (d) => onData(d, 'stdout'));
190
+ child.stderr.on('data', (d) => onData(d, 'stderr'));
191
+
192
+ child.on('error', (err) => {
193
+ clearTimeout(timeout);
194
+ resolve({ success: false, exitCode: -1, output: `${output}\n${err.message}`, elapsed: Date.now() - startTime });
195
+ });
196
+
197
+ child.on('close', (code) => {
198
+ clearTimeout(timeout);
199
+ resolve({
200
+ success: code === 0,
201
+ exitCode: code === null ? -1 : code,
202
+ output: output || '(no output)',
203
+ elapsed: Date.now() - startTime,
204
+ });
205
+ });
206
+ });
207
+ }
208
+
102
209
  executeCommand(command, dir = '', streamCallback = null) {
103
- if (!this.workerProcess) {
210
+ // Decide on transport.
211
+ if (!this.workerProcess && !this.useNativeFallback) {
104
212
  this.start();
105
- if (!this.workerProcess) {
106
- return Promise.resolve({ success: false, output: 'Worker process unavailable. Is the Go binary built?' });
107
- }
213
+ }
214
+ if (this.useNativeFallback || !this.workerProcess) {
215
+ return this._executeNative(command, dir, streamCallback);
108
216
  }
109
217
 
110
218
  return new Promise((resolve) => {
111
219
  const reqId = uuidv4();
112
220
 
113
- // Register handlers
114
221
  this.pendingRequests.set(reqId, {
115
222
  resolve,
116
223
  onStream: streamCallback,
117
- buffer: ''
224
+ buffer: '',
118
225
  });
119
226
 
120
227
  const req = {
121
228
  id: reqId,
122
- command: command,
123
- dir: dir,
229
+ command,
230
+ dir,
124
231
  stream: true,
125
- timeoutMs: 0
232
+ timeoutMs: 0,
126
233
  };
127
234
 
128
- // Send to Go via stdin
129
- this.workerProcess.stdin.write(JSON.stringify(req) + '\n');
235
+ try {
236
+ this.workerProcess.stdin.write(JSON.stringify(req) + '\n');
237
+ } catch (err) {
238
+ // Pipe broke mid-flight → fall back to native for this call.
239
+ this.pendingRequests.delete(reqId);
240
+ this.useNativeFallback = true;
241
+ this._executeNative(command, dir, streamCallback).then(resolve);
242
+ }
130
243
  });
131
244
  }
132
245
 
@@ -138,6 +251,5 @@ class WorkerClient {
138
251
  }
139
252
  }
140
253
 
141
- // Export singleton instance
142
254
  const workerClient = new WorkerClient();
143
255
  module.exports = workerClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samarthya-bot",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "SamarthyaBot — Privacy-First Local Agentic AI Operating System. Self-hosted multi-agent RPA engine with Telegram, Discord, Web Dashboard, Puppeteer browser control, SSH deployment, encrypted memory, voice transcription, and Indian workflow automation (GST, UPI, IRCTC). Supports Gemini, Claude, GPT, Ollama, DeepSeek, Qwen, OpenRouter. Made in India.",
5
5
  "main": "backend/server.js",
6
6
  "bin": {
@@ -155,4 +155,4 @@
155
155
  "type": "git",
156
156
  "url": "git+https://github.com/mebishnusahu0595/SamarthyaBot.git"
157
157
  }
158
- }
158
+ }