@intranefr/superbackend 1.5.2 → 1.5.3

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 (41) hide show
  1. package/index.js +2 -0
  2. package/manage.js +745 -0
  3. package/package.json +4 -2
  4. package/src/controllers/admin.controller.js +11 -5
  5. package/src/controllers/adminAgents.controller.js +37 -0
  6. package/src/controllers/adminLlm.controller.js +19 -0
  7. package/src/controllers/adminMarkdowns.controller.js +157 -0
  8. package/src/controllers/adminScripts.controller.js +138 -0
  9. package/src/controllers/adminTelegram.controller.js +72 -0
  10. package/src/controllers/markdowns.controller.js +42 -0
  11. package/src/helpers/mongooseHelper.js +6 -6
  12. package/src/helpers/scriptBase.js +2 -2
  13. package/src/middleware.js +136 -29
  14. package/src/models/Agent.js +105 -0
  15. package/src/models/AgentMessage.js +82 -0
  16. package/src/models/Markdown.js +75 -0
  17. package/src/models/ScriptRun.js +8 -0
  18. package/src/models/TelegramBot.js +42 -0
  19. package/src/routes/adminAgents.routes.js +13 -0
  20. package/src/routes/adminLlm.routes.js +1 -0
  21. package/src/routes/adminMarkdowns.routes.js +16 -0
  22. package/src/routes/adminScripts.routes.js +4 -1
  23. package/src/routes/adminTelegram.routes.js +14 -0
  24. package/src/routes/markdowns.routes.js +16 -0
  25. package/src/services/agent.service.js +546 -0
  26. package/src/services/agentHistory.service.js +345 -0
  27. package/src/services/agentTools.service.js +578 -0
  28. package/src/services/jsonConfigs.service.js +22 -10
  29. package/src/services/llm.service.js +219 -6
  30. package/src/services/markdowns.service.js +522 -0
  31. package/src/services/scriptsRunner.service.js +328 -37
  32. package/src/services/telegram.service.js +130 -0
  33. package/views/admin-agents.ejs +273 -0
  34. package/views/admin-coolify-deploy.ejs +8 -8
  35. package/views/admin-dashboard.ejs +36 -5
  36. package/views/admin-experiments.ejs +1 -1
  37. package/views/admin-markdowns.ejs +905 -0
  38. package/views/admin-scripts.ejs +221 -4
  39. package/views/admin-telegram.ejs +269 -0
  40. package/views/partials/dashboard/nav-items.ejs +3 -0
  41. package/analysis-only.skill +0 -0
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@intranefr/superbackend",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Node.js middleware that gives your project backend superpowers",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "start": "node server.js",
8
- "dev": "nodemon --verbose --ignore uploads --ignore '*.log' server.js",
8
+ "dev": "nodemon --verbose --ignore uploads --ignore stdout.log --ignore '*.log' server.js",
9
9
  "start:minio": "docker compose -f compose.standalone.yml --profile minio-only up -d minio",
10
10
  "minio:envs": "node -e \"console.log(['S3_ENDPOINT=http://localhost:9000','S3_REGION=us-east-1','S3_ACCESS_KEY_ID=minioadmin','S3_SECRET_ACCESS_KEY=minioadmin','S3_BUCKET=saasbackend','S3_FORCE_PATH_STYLE=true'].join('\\n'))\"",
11
11
  "build:sdk:error-tracking:browser": "esbuild sdk/error-tracking/browser/src/embed.js --bundle --format=iife --global-name=saasbackendErrorTrackingEmbed --outfile=sdk/error-tracking/browser/dist/embed.iife.js",
@@ -47,11 +47,13 @@
47
47
  "mysql2": "^3.16.1",
48
48
  "node-cron": "^4.2.1",
49
49
  "node-pty": "^1.1.0",
50
+ "node-telegram-bot-api": "^0.67.0",
50
51
  "openai": "^4.0.0",
51
52
  "redis": "^4.7.1",
52
53
  "resend": "^6.4.0",
53
54
  "ssh2-sftp-client": "^12.0.1",
54
55
  "stripe": "^14.0.0",
56
+ "terminal-kit": "^3.1.2",
55
57
  "vm2": "^3.10.0",
56
58
  "ws": "^8.18.0"
57
59
  },
@@ -352,7 +352,7 @@ const getWebhookStats = asyncHandler(async (req, res) => {
352
352
  const provisionCoolifyDeploy = asyncHandler(async (req, res) => {
353
353
  try {
354
354
  const { overwrite } = req.body;
355
- const managePath = path.join(process.cwd(), "manage.sh");
355
+ const managePath = path.join(process.cwd(), "manage.js");
356
356
  const exists = fs.existsSync(managePath);
357
357
 
358
358
  if (exists && !overwrite) {
@@ -363,13 +363,19 @@ const provisionCoolifyDeploy = asyncHandler(async (req, res) => {
363
363
  });
364
364
  }
365
365
 
366
- // In ref-superbackend, manage.sh already exists in the root of the repository
367
- // If it didn't, we would write it here. For this case, we'll just success.
366
+ // Copy the improved manage.js from polybot
367
+ const sourceManageJs = path.join(__dirname, "../../../manage.js");
368
+ if (fs.existsSync(sourceManageJs)) {
369
+ fs.copyFileSync(sourceManageJs, managePath);
370
+ // Make it executable
371
+ fs.chmodSync(managePath, '755');
372
+ }
373
+
368
374
  res.json({
369
375
  success: true,
370
376
  message: exists
371
- ? "Coolify Headless Deploy script (manage.sh) was already there."
372
- : "Coolify Headless Deploy script (manage.sh) is ready in the root directory.",
377
+ ? "Coolify Headless Deploy script (manage.js) was updated."
378
+ : "Coolify Headless Deploy script (manage.js) is ready in the root directory.",
373
379
  path: managePath,
374
380
  });
375
381
  } catch (error) {
@@ -0,0 +1,37 @@
1
+ const Agent = require('../models/Agent');
2
+
3
+ exports.listAgents = async (req, res) => {
4
+ try {
5
+ const agents = await Agent.find().lean();
6
+ return res.json({ items: agents });
7
+ } catch (error) {
8
+ return res.status(500).json({ error: error.message });
9
+ }
10
+ };
11
+
12
+ exports.createAgent = async (req, res) => {
13
+ try {
14
+ const agent = await Agent.create(req.body);
15
+ return res.json(agent);
16
+ } catch (error) {
17
+ return res.status(500).json({ error: error.message });
18
+ }
19
+ };
20
+
21
+ exports.updateAgent = async (req, res) => {
22
+ try {
23
+ const agent = await Agent.findByIdAndUpdate(req.params.id, req.body, { new: true });
24
+ return res.json(agent);
25
+ } catch (error) {
26
+ return res.status(500).json({ error: error.message });
27
+ }
28
+ };
29
+
30
+ exports.deleteAgent = async (req, res) => {
31
+ try {
32
+ await Agent.findByIdAndDelete(req.params.id);
33
+ return res.json({ success: true });
34
+ } catch (error) {
35
+ return res.status(500).json({ error: error.message });
36
+ }
37
+ };
@@ -387,6 +387,24 @@ async function listCosts(req, res) {
387
387
  }
388
388
  }
389
389
 
390
+ async function listProviders(req, res) {
391
+ try {
392
+ const providers = await getJsonSetting(PROVIDERS_KEY, {});
393
+ const safeProviders = {};
394
+ if (providers && typeof providers === "object") {
395
+ for (const [key, value] of Object.entries(providers)) {
396
+ if (!value || typeof value !== "object") continue;
397
+ const { apiKey, ...rest } = value;
398
+ safeProviders[key] = rest;
399
+ }
400
+ }
401
+ res.json({ providers: safeProviders });
402
+ } catch (error) {
403
+ console.error("[adminLlm] listProviders error", error);
404
+ res.status(500).json({ error: "Failed to load providers" });
405
+ }
406
+ }
407
+
390
408
  module.exports = {
391
409
  getConfig,
392
410
  saveConfig,
@@ -394,4 +412,5 @@ module.exports = {
394
412
  listAudit,
395
413
  listCosts,
396
414
  listOpenRouterModels,
415
+ listProviders,
397
416
  };
@@ -0,0 +1,157 @@
1
+ const {
2
+ ERROR_CODES,
3
+ listMarkdowns,
4
+ getMarkdownById,
5
+ createMarkdown,
6
+ updateMarkdown,
7
+ deleteMarkdown,
8
+ getFolderContents,
9
+ getUniqueGroupCodes,
10
+ validatePathUniqueness,
11
+ } = require('../services/markdowns.service');
12
+
13
+ function handleServiceError(res, error) {
14
+ const msg = error?.message || 'Operation failed';
15
+ const code = error?.code;
16
+
17
+ if (code === 'VALIDATION' || code === 'INVALID_MARKDOWN' || code === 'INVALID_GROUP_CODE') {
18
+ return res.status(400).json({ error: msg });
19
+ }
20
+ if (code === 'NOT_FOUND') {
21
+ return res.status(404).json({ error: msg });
22
+ }
23
+ if (code === 'PATH_NOT_UNIQUE') {
24
+ return res.status(409).json({ error: msg });
25
+ }
26
+
27
+ return res.status(500).json({ error: msg });
28
+ }
29
+
30
+ function parseJsonMaybe(value) {
31
+ if (value === undefined || value === null) return null;
32
+ if (typeof value !== 'string') return value;
33
+ const trimmed = value.trim();
34
+ if (!trimmed) return null;
35
+ try {
36
+ return JSON.parse(trimmed);
37
+ } catch (_) {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ exports.list = async (req, res) => {
43
+ try {
44
+ const filters = {
45
+ category: req.query.category,
46
+ group_code: req.query.group_code,
47
+ status: req.query.status,
48
+ ownerUserId: req.query.ownerUserId,
49
+ orgId: req.query.orgId,
50
+ search: req.query.search,
51
+ };
52
+
53
+ const pagination = {
54
+ page: Number(req.query.page) || 1,
55
+ limit: Number(req.query.limit) || 50,
56
+ sort: parseJsonMaybe(req.query.sort) || { updatedAt: -1 },
57
+ };
58
+
59
+ const result = await listMarkdowns(filters, pagination, { isAdmin: true });
60
+ return res.json(result);
61
+ } catch (error) {
62
+ console.error('Error listing markdowns:', error);
63
+ return handleServiceError(res, error);
64
+ }
65
+ };
66
+
67
+ exports.get = async (req, res) => {
68
+ try {
69
+ const item = await getMarkdownById(req.params.id);
70
+ if (!item) return res.status(404).json({ error: 'Markdown not found' });
71
+ return res.json({ item });
72
+ } catch (error) {
73
+ console.error('Error fetching markdown:', error);
74
+ return handleServiceError(res, error);
75
+ }
76
+ };
77
+
78
+ exports.create = async (req, res) => {
79
+ try {
80
+ const item = await createMarkdown(req.body || {});
81
+ return res.status(201).json({ item });
82
+ } catch (error) {
83
+ console.error('Error creating markdown:', error);
84
+ return handleServiceError(res, error);
85
+ }
86
+ };
87
+
88
+ exports.update = async (req, res) => {
89
+ try {
90
+ const item = await updateMarkdown(req.params.id, req.body || {});
91
+ return res.json({ item });
92
+ } catch (error) {
93
+ console.error('Error updating markdown:', error);
94
+ return handleServiceError(res, error);
95
+ }
96
+ };
97
+
98
+ exports.remove = async (req, res) => {
99
+ try {
100
+ const result = await deleteMarkdown(req.params.id);
101
+ return res.json(result);
102
+ } catch (error) {
103
+ console.error('Error deleting markdown:', error);
104
+ return handleServiceError(res, error);
105
+ }
106
+ };
107
+
108
+ exports.getFolderContents = async (req, res) => {
109
+ try {
110
+ const { category } = req.params;
111
+ const { group_code } = req.params;
112
+
113
+ const pagination = {
114
+ page: Number(req.query.page) || 1,
115
+ limit: Number(req.query.limit) || 100,
116
+ sort: parseJsonMaybe(req.query.sort) || { title: 1 },
117
+ };
118
+
119
+ const result = await getFolderContents(category, group_code, pagination, { isAdmin: true });
120
+ return res.json(result);
121
+ } catch (error) {
122
+ console.error('Error getting folder contents:', error);
123
+ return handleServiceError(res, error);
124
+ }
125
+ };
126
+
127
+ exports.validatePath = async (req, res) => {
128
+ try {
129
+ const { category, group_code, slug, excludeId } = req.body;
130
+
131
+ if (!category || !slug) {
132
+ return res.status(400).json({ error: 'category and slug are required' });
133
+ }
134
+
135
+ const isUnique = await validatePathUniqueness(category, group_code, slug, excludeId);
136
+ return res.json({ unique: isUnique });
137
+ } catch (error) {
138
+ console.error('Error validating path:', error);
139
+ return handleServiceError(res, error);
140
+ }
141
+ };
142
+
143
+ exports.getGroupCodes = async (req, res) => {
144
+ try {
145
+ const { category } = req.params;
146
+
147
+ if (!category) {
148
+ return res.status(400).json({ error: 'category is required' });
149
+ }
150
+
151
+ const groupCodes = await getUniqueGroupCodes(category, { isAdmin: true });
152
+ return res.json(groupCodes);
153
+ } catch (error) {
154
+ console.error('Error getting group codes:', error);
155
+ return handleServiceError(res, error);
156
+ }
157
+ };
@@ -349,3 +349,141 @@ exports.streamRun = async (req, res) => {
349
349
  return res.end();
350
350
  }
351
351
  };
352
+
353
+ // Get programmatic output (clean result for API consumption)
354
+ async function getProgrammaticOutput(req, res) {
355
+ try {
356
+ const { runId } = req.params;
357
+ if (!runId) {
358
+ return res.status(400).json({ error: 'runId is required' });
359
+ }
360
+
361
+ const run = await ScriptRun.findById(runId).lean();
362
+ if (!run) {
363
+ return res.status(404).json({ error: 'Script run not found' });
364
+ }
365
+
366
+ // Parse programmatic output if it's JSON
367
+ let parsedResult = null;
368
+ let isJson = false;
369
+
370
+ if (run.programmaticOutput) {
371
+ try {
372
+ parsedResult = JSON.parse(run.programmaticOutput);
373
+ isJson = true;
374
+ } catch {
375
+ // Not JSON, keep as string
376
+ parsedResult = null;
377
+ isJson = false;
378
+ }
379
+ }
380
+
381
+ res.json({
382
+ runId: run._id,
383
+ status: run.status,
384
+ exitCode: run.exitCode,
385
+ programmaticOutput: run.programmaticOutput || 'No output',
386
+ outputType: run.outputType || 'none',
387
+ isJson: isJson,
388
+ parsedResult: parsedResult,
389
+ returnResult: run.returnResult,
390
+ lastConsoleLog: run.lastConsoleLog,
391
+ createdAt: run.createdAt,
392
+ updatedAt: run.updatedAt,
393
+ startedAt: run.startedAt,
394
+ finishedAt: run.finishedAt
395
+ });
396
+ } catch (err) {
397
+ console.error('Error getting programmatic output:', err);
398
+ res.status(500).json({ error: err?.message || 'Internal server error' });
399
+ }
400
+ }
401
+
402
+ // Get full script output
403
+ async function getFullOutput(req, res) {
404
+ try {
405
+ const { runId } = req.params;
406
+ if (!runId) {
407
+ return res.status(400).json({ error: 'runId is required' });
408
+ }
409
+
410
+ const run = await ScriptRun.findById(runId).lean();
411
+ if (!run) {
412
+ return res.status(404).json({ error: 'Script run not found' });
413
+ }
414
+
415
+ res.json({
416
+ runId: run._id,
417
+ status: run.status,
418
+ exitCode: run.exitCode,
419
+ fullOutput: run.fullOutput || '',
420
+ outputSize: run.outputSize || 0,
421
+ lineCount: run.lineCount || 0,
422
+ lastOutputUpdate: run.lastOutputUpdate,
423
+ createdAt: run.createdAt,
424
+ updatedAt: run.updatedAt,
425
+ startedAt: run.startedAt,
426
+ finishedAt: run.finishedAt
427
+ });
428
+ } catch (err) {
429
+ console.error('Error getting full output:', err);
430
+ res.status(500).json({ error: err?.message || 'Internal server error' });
431
+ }
432
+ }
433
+
434
+ // Download script output as file
435
+ async function downloadOutput(req, res) {
436
+ try {
437
+ const { runId } = req.params;
438
+ if (!runId) {
439
+ return res.status(400).json({ error: 'runId is required' });
440
+ }
441
+
442
+ const run = await ScriptRun.findById(runId).lean();
443
+ if (!run) {
444
+ return res.status(404).json({ error: 'Script run not found' });
445
+ }
446
+
447
+ const filename = `script-output-${runId}-${run.createdAt.toISOString().slice(0, 19).replace(/:/g, '-')}.txt`;
448
+
449
+ res.setHeader('Content-Type', 'text/plain');
450
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
451
+
452
+ // Include metadata at the top
453
+ const metadata = [
454
+ `Script Run ID: ${runId}`,
455
+ `Status: ${run.status}`,
456
+ `Exit Code: ${run.exitCode || 'N/A'}`,
457
+ `Started: ${run.startedAt || 'N/A'}`,
458
+ `Finished: ${run.finishedAt || 'N/A'}`,
459
+ `Output Size: ${run.outputSize || 0} characters`,
460
+ `Line Count: ${run.lineCount || 0}`,
461
+ `Created: ${run.createdAt}`,
462
+ '=' .repeat(50),
463
+ ''
464
+ ].join('\n');
465
+
466
+ res.send(metadata + (run.fullOutput || run.outputTail || 'No output available'));
467
+ } catch (err) {
468
+ console.error('Error downloading output:', err);
469
+ res.status(500).json({ error: err?.message || 'Internal server error' });
470
+ }
471
+ }
472
+
473
+ exports.getFullOutput = getFullOutput;
474
+ exports.downloadOutput = downloadOutput;
475
+
476
+ module.exports = {
477
+ listScripts: exports.listScripts,
478
+ getScript: exports.getScript,
479
+ createScript: exports.createScript,
480
+ updateScript: exports.updateScript,
481
+ deleteScript: exports.deleteScript,
482
+ runScript: exports.runScript,
483
+ listRuns: exports.listRuns,
484
+ getRun: exports.getRun,
485
+ streamRunLogs: exports.streamRun,
486
+ getProgrammaticOutput: getProgrammaticOutput,
487
+ getFullOutput: exports.getFullOutput,
488
+ downloadOutput: exports.downloadOutput,
489
+ };
@@ -0,0 +1,72 @@
1
+ const TelegramBot = require('../models/TelegramBot');
2
+ const Agent = require('../models/Agent');
3
+ const telegramService = require('../services/telegram.service');
4
+
5
+ exports.listBots = async (req, res) => {
6
+ try {
7
+ const bots = await TelegramBot.find().populate('defaultAgentId', 'name').lean();
8
+ return res.json({ items: bots });
9
+ } catch (error) {
10
+ return res.status(500).json({ error: error.message });
11
+ }
12
+ };
13
+
14
+ exports.createBot = async (req, res) => {
15
+ try {
16
+ const data = { ...req.body };
17
+ if (data.defaultAgentId === '') delete data.defaultAgentId;
18
+
19
+ const bot = await TelegramBot.create(data);
20
+ if (bot.isActive) {
21
+ await telegramService.startBot(bot._id);
22
+ }
23
+ return res.json(bot);
24
+ } catch (error) {
25
+ return res.status(500).json({ error: error.message });
26
+ }
27
+ };
28
+
29
+ exports.updateBot = async (req, res) => {
30
+ try {
31
+ const data = { ...req.body };
32
+ if (data.defaultAgentId === '') data.defaultAgentId = null;
33
+
34
+ const bot = await TelegramBot.findByIdAndUpdate(req.params.id, data, { new: true });
35
+ if (bot.isActive) {
36
+ await telegramService.startBot(bot._id);
37
+ } else {
38
+ await telegramService.stopBot(bot._id);
39
+ }
40
+ return res.json(bot);
41
+ } catch (error) {
42
+ return res.status(500).json({ error: error.message });
43
+ }
44
+ };
45
+
46
+ exports.deleteBot = async (req, res) => {
47
+ try {
48
+ await telegramService.stopBot(req.params.id);
49
+ await TelegramBot.findByIdAndDelete(req.params.id);
50
+ return res.json({ success: true });
51
+ } catch (error) {
52
+ return res.status(500).json({ error: error.message });
53
+ }
54
+ };
55
+
56
+ exports.toggleBot = async (req, res) => {
57
+ try {
58
+ const bot = await TelegramBot.findById(req.params.id);
59
+ bot.isActive = !bot.isActive;
60
+ await bot.save();
61
+
62
+ if (bot.isActive) {
63
+ await telegramService.startBot(bot._id);
64
+ } else {
65
+ await telegramService.stopBot(bot._id);
66
+ }
67
+
68
+ return res.json(bot);
69
+ } catch (error) {
70
+ return res.status(500).json({ error: error.message });
71
+ }
72
+ };
@@ -0,0 +1,42 @@
1
+ const { getMarkdownByPath, searchMarkdowns } = require('../services/markdowns.service');
2
+
3
+ exports.getByPath = async (req, res) => {
4
+ try {
5
+ const { category, group_code, slug } = req.params;
6
+ // Check if JSON is requested via query or if we are on a .json route
7
+ const isJson = req.query?.json === 'true' || req.query?.json === '1' || req.path.endsWith('/json');
8
+
9
+ const doc = await getMarkdownByPath(category, group_code, slug);
10
+
11
+ if (isJson) {
12
+ return res.json({ item: doc });
13
+ }
14
+
15
+ // Serve raw markdown with correct MIME type
16
+ return res.type('text/markdown').send(doc.markdownRaw);
17
+ } catch (error) {
18
+ const code = error?.code;
19
+ if (code === 'NOT_FOUND') {
20
+ return res.status(404).json({ error: 'Markdown not found' });
21
+ }
22
+
23
+ console.error('Error fetching markdown:', error);
24
+ return res.status(500).json({ error: error?.message || 'Failed to fetch markdown' });
25
+ }
26
+ };
27
+
28
+ exports.search = async (req, res) => {
29
+ try {
30
+ const { q: query, category, group_code, limit = 50 } = req.query;
31
+
32
+ if (!query) {
33
+ return res.status(400).json({ error: 'Search query (q) is required' });
34
+ }
35
+
36
+ const results = await searchMarkdowns(query, { category, group_code, limit: Number(limit) });
37
+ return res.json({ results });
38
+ } catch (error) {
39
+ console.error('Error searching markdowns:', error);
40
+ return res.status(500).json({ error: error?.message || 'Failed to search markdowns' });
41
+ }
42
+ };
@@ -83,7 +83,7 @@ class MongooseHelper {
83
83
  const uri = this.getMongoUri();
84
84
  const options = this.getConnectionOptions();
85
85
 
86
- console.log(`[MongooseHelper] Connecting to MongoDB...`);
86
+ if (!process.env.TUI_MODE) console.log(`[MongooseHelper] Connecting to MongoDB...`);
87
87
 
88
88
  // Clear any existing connection
89
89
  if (mongoose.connection.readyState !== 0) {
@@ -94,7 +94,7 @@ class MongooseHelper {
94
94
 
95
95
  this.isConnected = true;
96
96
 
97
- console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
97
+ if (!process.env.TUI_MODE) console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
98
98
 
99
99
  // Setup connection error handling
100
100
  mongoose.connection.on('error', (error) => {
@@ -104,13 +104,13 @@ class MongooseHelper {
104
104
  });
105
105
 
106
106
  mongoose.connection.on('disconnected', () => {
107
- console.log('[MongooseHelper] Disconnected from MongoDB');
107
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] Disconnected from MongoDB');
108
108
  this.isConnected = false;
109
109
  this.connectionPromise = null;
110
110
  });
111
111
 
112
112
  mongoose.connection.on('reconnected', () => {
113
- console.log('[MongooseHelper] Reconnected to MongoDB');
113
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] Reconnected to MongoDB');
114
114
  this.isConnected = true;
115
115
  });
116
116
 
@@ -136,7 +136,7 @@ class MongooseHelper {
136
136
  if (this.connectionCount <= 0) {
137
137
  try {
138
138
  await mongoose.disconnect();
139
- console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
139
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
140
140
  } catch (error) {
141
141
  console.error('[MongooseHelper] Disconnect error:', error);
142
142
  } finally {
@@ -155,7 +155,7 @@ class MongooseHelper {
155
155
  try {
156
156
  if (mongoose.connection.readyState !== 0) {
157
157
  await mongoose.disconnect();
158
- console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
158
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
159
159
  }
160
160
  } catch (error) {
161
161
  console.error('[MongooseHelper] Force disconnect error:', error);
@@ -55,13 +55,13 @@ class ScriptBase {
55
55
  });
56
56
 
57
57
  try {
58
- console.log(`[${this.name}] Starting script execution...`);
58
+ if (!process.env.TUI_MODE) console.log(`[${this.name}] Starting script execution...`);
59
59
 
60
60
  const executionPromise = this._executeWithConnection();
61
61
  const result = await Promise.race([executionPromise, timeoutPromise]);
62
62
 
63
63
  const duration = Date.now() - this.startTime;
64
- console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
64
+ if (!process.env.TUI_MODE) console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
65
65
 
66
66
  return result;
67
67
  } catch (error) {