@intranefr/superbackend 1.5.2 → 1.6.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 (134) hide show
  1. package/cookies.txt +6 -0
  2. package/cookies1.txt +6 -0
  3. package/cookies2.txt +6 -0
  4. package/cookies3.txt +6 -0
  5. package/cookies4.txt +5 -0
  6. package/cookies_old.txt +5 -0
  7. package/cookies_old_test.txt +6 -0
  8. package/cookies_super.txt +5 -0
  9. package/cookies_super_test.txt +6 -0
  10. package/cookies_test.txt +6 -0
  11. package/index.js +9 -0
  12. package/manage.js +745 -0
  13. package/package.json +6 -2
  14. package/plugins/core-waiting-list-migration/README.md +118 -0
  15. package/plugins/core-waiting-list-migration/index.js +438 -0
  16. package/plugins/global-settings-presets/index.js +20 -0
  17. package/plugins/hello-cli/index.js +17 -0
  18. package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
  19. package/plugins/ui-components-seeder/components/suiToast.js +186 -0
  20. package/plugins/ui-components-seeder/index.js +31 -0
  21. package/public/js/admin-ui-components-preview.js +281 -0
  22. package/public/js/admin-ui-components.js +408 -0
  23. package/public/js/llm-provider-model-picker.js +193 -0
  24. package/public/test-iframe-fix.html +63 -0
  25. package/public/test-iframe.html +14 -0
  26. package/src/admin/endpointRegistry.js +68 -0
  27. package/src/controllers/admin.controller.js +36 -10
  28. package/src/controllers/adminAgents.controller.js +37 -0
  29. package/src/controllers/adminDataCleanup.controller.js +45 -0
  30. package/src/controllers/adminLlm.controller.js +19 -8
  31. package/src/controllers/adminLogin.controller.js +269 -0
  32. package/src/controllers/adminMarkdowns.controller.js +157 -0
  33. package/src/controllers/adminPlugins.controller.js +55 -0
  34. package/src/controllers/adminRegistry.controller.js +106 -0
  35. package/src/controllers/adminScripts.controller.js +138 -0
  36. package/src/controllers/adminStats.controller.js +4 -4
  37. package/src/controllers/adminTelegram.controller.js +72 -0
  38. package/src/controllers/markdowns.controller.js +42 -0
  39. package/src/controllers/registry.controller.js +32 -0
  40. package/src/controllers/waitingList.controller.js +52 -74
  41. package/src/helpers/mongooseHelper.js +6 -6
  42. package/src/helpers/scriptBase.js +2 -2
  43. package/src/middleware/auth.js +71 -1
  44. package/src/middleware/rbac.js +62 -0
  45. package/src/middleware.js +584 -176
  46. package/src/models/Agent.js +105 -0
  47. package/src/models/AgentMessage.js +82 -0
  48. package/src/models/GlobalSetting.js +11 -1
  49. package/src/models/Markdown.js +75 -0
  50. package/src/models/ScriptRun.js +8 -0
  51. package/src/models/TelegramBot.js +42 -0
  52. package/src/models/UiComponent.js +2 -0
  53. package/src/models/User.js +1 -1
  54. package/src/routes/admin.routes.js +3 -3
  55. package/src/routes/adminAgents.routes.js +13 -0
  56. package/src/routes/adminAssets.routes.js +11 -11
  57. package/src/routes/adminBlog.routes.js +2 -2
  58. package/src/routes/adminBlogAi.routes.js +2 -2
  59. package/src/routes/adminBlogAutomation.routes.js +2 -2
  60. package/src/routes/adminCache.routes.js +2 -2
  61. package/src/routes/adminConsoleManager.routes.js +2 -2
  62. package/src/routes/adminCrons.routes.js +2 -2
  63. package/src/routes/adminDataCleanup.routes.js +26 -0
  64. package/src/routes/adminDbBrowser.routes.js +2 -2
  65. package/src/routes/adminEjsVirtual.routes.js +2 -2
  66. package/src/routes/adminFeatureFlags.routes.js +6 -6
  67. package/src/routes/adminHeadless.routes.js +2 -2
  68. package/src/routes/adminHealthChecks.routes.js +2 -2
  69. package/src/routes/adminI18n.routes.js +2 -2
  70. package/src/routes/adminJsonConfigs.routes.js +8 -8
  71. package/src/routes/adminLlm.routes.js +8 -7
  72. package/src/routes/adminLogin.routes.js +23 -0
  73. package/src/routes/adminMarkdowns.routes.js +10 -0
  74. package/src/routes/adminMigration.routes.js +12 -12
  75. package/src/routes/adminPages.routes.js +2 -2
  76. package/src/routes/adminPlugins.routes.js +15 -0
  77. package/src/routes/adminProxy.routes.js +2 -2
  78. package/src/routes/adminRateLimits.routes.js +8 -8
  79. package/src/routes/adminRbac.routes.js +2 -2
  80. package/src/routes/adminRegistry.routes.js +24 -0
  81. package/src/routes/adminScripts.routes.js +6 -3
  82. package/src/routes/adminSeoConfig.routes.js +10 -10
  83. package/src/routes/adminTelegram.routes.js +14 -0
  84. package/src/routes/adminTerminals.routes.js +2 -2
  85. package/src/routes/adminUiComponents.routes.js +2 -2
  86. package/src/routes/adminUploadNamespaces.routes.js +7 -7
  87. package/src/routes/blogInternal.routes.js +2 -2
  88. package/src/routes/experiments.routes.js +2 -2
  89. package/src/routes/formsAdmin.routes.js +6 -6
  90. package/src/routes/globalSettings.routes.js +8 -8
  91. package/src/routes/internalExperiments.routes.js +2 -2
  92. package/src/routes/markdowns.routes.js +16 -0
  93. package/src/routes/notificationAdmin.routes.js +7 -7
  94. package/src/routes/orgAdmin.routes.js +16 -16
  95. package/src/routes/pages.routes.js +3 -3
  96. package/src/routes/registry.routes.js +11 -0
  97. package/src/routes/stripeAdmin.routes.js +12 -12
  98. package/src/routes/userAdmin.routes.js +7 -7
  99. package/src/routes/waitingListAdmin.routes.js +2 -2
  100. package/src/routes/workflows.routes.js +3 -3
  101. package/src/services/agent.service.js +546 -0
  102. package/src/services/agentHistory.service.js +345 -0
  103. package/src/services/agentTools.service.js +578 -0
  104. package/src/services/dataCleanup.service.js +286 -0
  105. package/src/services/jsonConfigs.service.js +284 -10
  106. package/src/services/llm.service.js +219 -6
  107. package/src/services/markdowns.service.js +522 -0
  108. package/src/services/plugins.service.js +348 -0
  109. package/src/services/registry.service.js +452 -0
  110. package/src/services/scriptsRunner.service.js +328 -37
  111. package/src/services/telegram.service.js +130 -0
  112. package/src/services/uiComponents.service.js +180 -0
  113. package/src/services/waitingListJson.service.js +401 -0
  114. package/src/utils/rbac/rightsRegistry.js +118 -0
  115. package/test-access.js +63 -0
  116. package/test-iframe-fix.html +63 -0
  117. package/test-iframe.html +14 -0
  118. package/views/admin-403.ejs +92 -0
  119. package/views/admin-agents.ejs +273 -0
  120. package/views/admin-coolify-deploy.ejs +8 -8
  121. package/views/admin-dashboard-home.ejs +52 -2
  122. package/views/admin-dashboard.ejs +179 -7
  123. package/views/admin-data-cleanup.ejs +357 -0
  124. package/views/admin-experiments.ejs +1 -1
  125. package/views/admin-login.ejs +286 -0
  126. package/views/admin-markdowns.ejs +905 -0
  127. package/views/admin-plugins-system.ejs +223 -0
  128. package/views/admin-scripts.ejs +221 -4
  129. package/views/admin-telegram.ejs +269 -0
  130. package/views/admin-ui-components.ejs +82 -402
  131. package/views/admin-users.ejs +207 -11
  132. package/views/partials/dashboard/nav-items.ejs +5 -0
  133. package/views/partials/llm-provider-model-picker.ejs +0 -161
  134. package/analysis-only.skill +0 -0
@@ -28,22 +28,51 @@ const MAX_TAIL_BYTES = 64 * 1024;
28
28
  }
29
29
 
30
30
  function wrapInAsyncIife(code) {
31
- const body = String(code || '');
32
- return [
33
- '(async () => {',
34
- body,
35
- '})().catch((err) => {',
36
- ' try { console.error(err && err.stack ? err.stack : err); } catch {}',
37
- '});',
38
- '',
39
- ].join('\n');
31
+ const body = String(code || '');
32
+ return [
33
+ '(async () => {',
34
+ body,
35
+ '})().then((result) => {',
36
+ ' // Store result globally for VM2 to capture',
37
+ ' global.__scriptResult = result;',
38
+ '}).catch((err) => {',
39
+ ' try { console.error(err && err.stack ? err.stack : err); } catch {}',
40
+ ' global.__scriptResult = { error: err.message || String(err) };',
41
+ '});',
42
+ '',
43
+ ].join('\n');
44
+ }
45
+
46
+ function wrapExistingAsyncIife(code) {
47
+ const body = String(code || '');
48
+ // Remove the final closing parenthesis and semicolon, then add our result capture
49
+ const codeWithoutEnding = body.replace(/\)\s*;?\s*$/, '');
50
+ return [
51
+ '// Wrapped to capture return value',
52
+ codeWithoutEnding,
53
+ ').then((result) => {',
54
+ ' // Store result globally for VM2 to capture',
55
+ ' global.__scriptResult = result;',
56
+ '}).catch((err) => {',
57
+ ' try { console.error(err && err.stack ? err.stack : err); } catch {}',
58
+ ' global.__scriptResult = { error: err.message || String(err) };',
59
+ '});',
60
+ '',
61
+ ].join('\n');
40
62
  }
41
63
 
42
64
  function prepareVmCodeForExecution(code) {
43
- const raw = String(code || '');
44
- if (!shouldAutoWrapAsyncScripts()) return { code: raw, wrapped: false };
45
- if (!detectTopLevelAwait(raw)) return { code: raw, wrapped: false };
46
- return { code: wrapInAsyncIife(raw), wrapped: true };
65
+ const raw = String(code || '');
66
+ if (!shouldAutoWrapAsyncScripts()) return { code: raw, wrapped: false };
67
+ if (!detectTopLevelAwait(raw)) return { code: raw, wrapped: false };
68
+
69
+ // Check if it's already an async IIFE that doesn't expose its result
70
+ if (/^\s*\(\s*async\s+function\s*\(/.test(raw) && !/global\.__scriptResult\s*=/.test(raw)) {
71
+ // Wrap the existing async IIFE to capture its result
72
+ return { code: wrapExistingAsyncIife(raw), wrapped: true };
73
+ }
74
+
75
+ return { code: wrapInAsyncIife(raw), wrapped: true };
47
76
  }
48
77
 
49
78
  function buildAwaitSyntaxHelpMessage() {
@@ -71,15 +100,88 @@ function decodeScriptContent(script, format) {
71
100
  return script;
72
101
  }
73
102
 
74
- function nowIso() {
75
- return new Date().toISOString();
103
+ const nowIso = () => new Date().toISOString();
104
+
105
+ const appendTail = (tail, more) => {
106
+ const max = 20000; // keep last 20k chars
107
+ tail = (tail + more).slice(-max);
108
+ return tail;
109
+ };
110
+
111
+ // Infrastructure log patterns to filter out
112
+ const infrastructurePatterns = [
113
+ 'Using existing app database connection',
114
+ 'No existing connection found',
115
+ 'Auto-wrapping script in async function',
116
+ '=== SCRIPT START ===',
117
+ '=== SCRIPT END ===',
118
+ 'Executing script',
119
+ 'Script preview',
120
+ 'Database connection established',
121
+ 'chars)',
122
+ 'Infrastructure logs'
123
+ ];
124
+
125
+ // Utility functions for output processing
126
+ function isInfrastructureLog(line) {
127
+ return infrastructurePatterns.some(pattern => line.includes(pattern));
76
128
  }
77
129
 
78
- function appendTail(prev, chunk) {
79
- const next = String(prev || '') + String(chunk || '');
80
- const buf = Buffer.from(next, 'utf8');
81
- if (buf.length <= MAX_TAIL_BYTES) return next;
82
- return buf.slice(buf.length - MAX_TAIL_BYTES).toString('utf8');
130
+ function isMeaningfulConsoleLog(line) {
131
+ return !isInfrastructureLog(line) &&
132
+ line.trim().length > 0 &&
133
+ !line.startsWith('[') &&
134
+ !line.includes('===');
135
+ }
136
+
137
+ function formatOutput(value) {
138
+ if (typeof value === 'object') {
139
+ return JSON.stringify(value, null, 2);
140
+ }
141
+ return String(value);
142
+ }
143
+
144
+ function tryParseJson(str) {
145
+ try {
146
+ return JSON.parse(str);
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+
152
+ function isJsonString(str) {
153
+ return tryParseJson(str) !== null;
154
+ }
155
+
156
+ function determineProgrammaticOutput(returnValue, lastConsoleLog) {
157
+ // Priority 1: Return value
158
+ if (returnValue !== undefined && returnValue !== null) {
159
+ const formatted = formatOutput(returnValue);
160
+ return {
161
+ programmaticOutput: formatted,
162
+ outputType: 'return',
163
+ isJson: isJsonString(formatted),
164
+ parsedResult: tryParseJson(formatted)
165
+ };
166
+ }
167
+
168
+ // Priority 2: Last meaningful console.log
169
+ if (lastConsoleLog && !isInfrastructureLog(lastConsoleLog)) {
170
+ return {
171
+ programmaticOutput: lastConsoleLog,
172
+ outputType: 'console',
173
+ isJson: isJsonString(lastConsoleLog),
174
+ parsedResult: tryParseJson(lastConsoleLog)
175
+ };
176
+ }
177
+
178
+ // Priority 3: No output
179
+ return {
180
+ programmaticOutput: 'No output',
181
+ outputType: 'none',
182
+ isJson: false,
183
+ parsedResult: null
184
+ };
83
185
  }
84
186
 
85
187
  function safeJsonParse(str) {
@@ -272,12 +374,31 @@ async function runSpawned({ runId, bus, command, args, env, cwd, timeoutMs }) {
272
374
 
273
375
  async function runHostWithDatabase({ runId, bus, code, env, cwd, timeoutMs }) {
274
376
  let tail = '';
377
+ let scriptResult = null;
378
+ let lastConsoleLog = null;
275
379
 
276
380
  function pushLog(stream, line) {
277
381
  const s = String(line || '');
278
382
  tail = appendTail(tail, s);
279
383
  bus.push({ type: 'log', ts: nowIso(), stream, line: s });
280
- return ScriptRun.updateOne({ _id: runId }, { $set: { outputTail: tail } });
384
+
385
+ // Track last console.log for programmatic output
386
+ if (stream === 'stdout' && isMeaningfulConsoleLog(s)) {
387
+ lastConsoleLog = s.trim();
388
+ }
389
+
390
+ // Update both outputTail and fullOutput using string concatenation
391
+ return ScriptRun.findById(runId).then(run => {
392
+ if (run) {
393
+ run.outputTail = tail;
394
+ run.fullOutput = (run.fullOutput || '') + s;
395
+ run.lastConsoleLog = lastConsoleLog;
396
+ run.lastOutputUpdate = new Date();
397
+ run.outputSize = (run.outputSize || 0) + s.length;
398
+ run.lineCount = (run.lineCount || 0) + (s.split('\n').length - 1);
399
+ return run.save();
400
+ }
401
+ });
281
402
  }
282
403
 
283
404
  try {
@@ -299,7 +420,7 @@ async function runHostWithDatabase({ runId, bus, code, env, cwd, timeoutMs }) {
299
420
 
300
421
  const prepared = prepareVmCodeForExecution(code);
301
422
  if (prepared.wrapped) {
302
- await pushLog('stdout', 'Auto-wrapping script in async function (detected top-level await)\n');
423
+ await pushLog('stdout', 'Auto-wrapping script to capture return value\n');
303
424
  }
304
425
 
305
426
  // Create a VM with database context
@@ -366,11 +487,21 @@ async function runHostWithDatabase({ runId, bus, code, env, cwd, timeoutMs }) {
366
487
  // Process environment
367
488
  process: {
368
489
  env: { ...process.env, ...env }
369
- }
490
+ },
491
+
492
+ // Debug: Log available models
493
+ debugModels: () => {
494
+ console.log('Available models:', Object.keys(mongoose.models || {}));
495
+ return mongoose.models || {};
496
+ },
497
+
498
+ // Global variables for async result capture
499
+ global: {},
500
+ __scriptResult: undefined
370
501
  },
371
502
  require: {
372
503
  external: false,
373
- builtin: ['util', 'path', 'os'], // Allow some basic built-ins
504
+ builtin: ['util', 'path', 'os', 'mongoose'], // Allow mongoose for parent app model access
374
505
  },
375
506
  timeout: timeoutMs,
376
507
  eval: false,
@@ -379,30 +510,138 @@ async function runHostWithDatabase({ runId, bus, code, env, cwd, timeoutMs }) {
379
510
 
380
511
  // Set up console redirection
381
512
  vm.on('console.log', (...args) => {
382
- pushLog('stdout', args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ') + '\n');
513
+ const message = args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ') + '\n';
514
+
515
+ // Send to UI output
516
+ pushLog('stdout', message);
517
+
518
+ // Also send to parent process (backend logs)
519
+ console.log('[Script]', message.trim());
383
520
  });
384
521
  vm.on('console.error', (...args) => {
385
- pushLog('stderr', args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ') + '\n');
522
+ const message = args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ') + '\n';
523
+
524
+ // Send to UI output
525
+ pushLog('stderr', message);
526
+
527
+ // Also send to parent process (backend logs)
528
+ console.error('[Script]', message.trim());
529
+ });
530
+
531
+ // Handle unhandled promise rejections within the VM
532
+ vm.on('unhandledRejection', (reason, promise) => {
533
+ let errorMsg = 'Unhandled Promise Rejection: ';
534
+
535
+ if (reason instanceof Error) {
536
+ errorMsg += reason.message;
537
+ if (reason.stack) {
538
+ errorMsg += '\n' + reason.stack;
539
+ }
540
+ } else {
541
+ errorMsg += JSON.stringify(reason);
542
+ }
543
+
544
+ // Send to UI output
545
+ pushLog('stderr', errorMsg + '\n');
546
+
547
+ // Also send to parent process (backend logs)
548
+ console.error('[Script]', errorMsg);
549
+ });
550
+
551
+ // Handle uncaught exceptions within the VM
552
+ vm.on('error', (error) => {
553
+ let errorMsg = 'VM Error: ';
554
+
555
+ if (error instanceof Error) {
556
+ errorMsg += error.message;
557
+ if (error.stack) {
558
+ errorMsg += '\n' + error.stack;
559
+ }
560
+ } else {
561
+ errorMsg += JSON.stringify(error);
562
+ }
563
+
564
+ // Send to UI output
565
+ pushLog('stderr', errorMsg + '\n');
566
+
567
+ // Also send to parent process (backend logs)
568
+ console.error('[Script]', errorMsg);
386
569
  });
387
570
 
388
571
  // Run the script code with better error handling
389
572
  try {
573
+ await pushLog('stdout', `=== SCRIPT START ===\n`);
574
+ await pushLog('stdout', `Executing script (${prepared.code.length} chars)...\n`);
575
+
576
+ // Show first few lines of script for debugging
577
+ const scriptPreview = prepared.code.split('\n').slice(0, 5).join('\n');
578
+ await pushLog('stdout', `Script preview:\n${scriptPreview}\n...\n`);
579
+
580
+ // Run the script
390
581
  vm.run(prepared.code, 'script.host.js');
582
+
583
+ // Capture result based on whether the script was wrapped
584
+ if (prepared.wrapped) {
585
+ // For wrapped scripts, wait and capture from global variable
586
+ await new Promise(resolve => setTimeout(resolve, 100)); // Wait for async completion
587
+ scriptResult = vm.sandbox.__scriptResult;
588
+ } else {
589
+ // For non-wrapped scripts, try to capture direct return or use global variable
590
+ scriptResult = vm.sandbox.__scriptResult;
591
+ // Also try to get the last meaningful console.log as fallback
592
+ if (!scriptResult && lastConsoleLog) {
593
+ scriptResult = lastConsoleLog;
594
+ }
595
+ }
596
+
597
+ await pushLog('stdout', `=== SCRIPT END ===\n`);
598
+
599
+ // Determine and save programmatic output
600
+ const programmaticOutput = determineProgrammaticOutput(scriptResult, lastConsoleLog);
601
+ await ScriptRun.updateOne(
602
+ { _id: runId },
603
+ {
604
+ $set: {
605
+ programmaticOutput: programmaticOutput.programmaticOutput,
606
+ returnResult: scriptResult !== undefined && scriptResult !== null ? formatOutput(scriptResult) : '',
607
+ outputType: programmaticOutput.outputType
608
+ }
609
+ }
610
+ );
391
611
  } catch (vmError) {
392
- const baseMsg = vmError?.message || 'Unknown VM error';
393
- const help = baseMsg.includes('await is only valid in async functions') ? `\n\n${buildAwaitSyntaxHelpMessage()}` : '';
394
- const errorMsg = `VM execution error: ${baseMsg}${help}`;
395
- await pushLog('stderr', errorMsg + '\n');
612
+ let errorMsg = 'VM execution error: ';
613
+
614
+ if (vmError instanceof Error) {
615
+ errorMsg += vmError.message;
616
+ if (vmError.stack) {
617
+ errorMsg += '\n' + vmError.stack;
618
+ }
619
+ } else {
620
+ errorMsg += JSON.stringify(vmError);
621
+ }
622
+
623
+ const help = vmError?.message?.includes('await is only valid in async functions') ? `\n\n${buildAwaitSyntaxHelpMessage()}` : '';
624
+ await pushLog('stderr', errorMsg + help + '\n');
396
625
  return 1;
397
626
  }
398
627
 
399
628
  return 0;
400
629
 
401
630
  } catch (err) {
402
- const msg = err?.message || 'Host script error';
403
- await pushLog('stderr', msg + '\n');
404
- return 1;
405
- } finally {
631
+ let errorMsg = 'Host script error: ';
632
+
633
+ if (err instanceof Error) {
634
+ errorMsg += err.message;
635
+ if (err.stack) {
636
+ errorMsg += '\n' + err.stack;
637
+ }
638
+ } else {
639
+ errorMsg += JSON.stringify(err);
640
+ }
641
+
642
+ await pushLog('stderr', errorMsg + '\n');
643
+ return 1;
644
+ } finally {
406
645
  // Don't disconnect here - let mongooseHelper manage connection pooling
407
646
  // The connection will be cleaned up when the helper decides
408
647
  }
@@ -410,17 +649,39 @@ async function runHostWithDatabase({ runId, bus, code, env, cwd, timeoutMs }) {
410
649
 
411
650
  async function runVm2({ runId, bus, code, timeoutMs }) {
412
651
  let tail = '';
652
+ let scriptResult = null;
653
+ let lastConsoleLog = null;
413
654
 
414
655
  function pushLog(stream, line) {
415
656
  const s = String(line || '');
416
657
  tail = appendTail(tail, s);
417
658
  bus.push({ type: 'log', ts: nowIso(), stream, line: s });
418
- return ScriptRun.updateOne({ _id: runId }, { $set: { outputTail: tail } });
659
+
660
+ // Track last console.log for programmatic output
661
+ if (stream === 'stdout' && isMeaningfulConsoleLog(s)) {
662
+ lastConsoleLog = s.trim();
663
+ }
664
+
665
+ // Update both outputTail and fullOutput using string concatenation
666
+ return ScriptRun.findById(runId).then(run => {
667
+ if (run) {
668
+ run.outputTail = tail;
669
+ run.fullOutput = (run.fullOutput || '') + s;
670
+ run.lastConsoleLog = lastConsoleLog;
671
+ run.lastOutputUpdate = new Date();
672
+ run.outputSize = (run.outputSize || 0) + s.length;
673
+ run.lineCount = (run.lineCount || 0) + (s.split('\n').length - 1);
674
+ return run.save();
675
+ }
676
+ });
419
677
  }
420
678
 
421
679
  const vm = new NodeVM({
422
680
  console: 'redirect',
423
- sandbox: {},
681
+ sandbox: {
682
+ global: {},
683
+ __scriptResult: undefined
684
+ },
424
685
  require: {
425
686
  external: false,
426
687
  builtin: [],
@@ -440,9 +701,39 @@ async function runVm2({ runId, bus, code, timeoutMs }) {
440
701
  try {
441
702
  const prepared = prepareVmCodeForExecution(code);
442
703
  if (prepared.wrapped) {
443
- await pushLog('stdout', 'Auto-wrapping script in async function (detected top-level await)\n');
704
+ await pushLog('stdout', 'Auto-wrapping script to capture return value\n');
444
705
  }
706
+
707
+ // Run the script
445
708
  vm.run(prepared.code, 'script.vm2.js');
709
+
710
+ // Capture result based on whether the script was wrapped
711
+ if (prepared.wrapped) {
712
+ // For wrapped scripts, wait and capture from global variable
713
+ await new Promise(resolve => setTimeout(resolve, 100)); // Wait for async completion
714
+ scriptResult = vm.sandbox.__scriptResult;
715
+ } else {
716
+ // For non-wrapped scripts, try to capture direct return or use global variable
717
+ scriptResult = vm.sandbox.__scriptResult;
718
+ // Also try to get the last meaningful console.log as fallback
719
+ if (!scriptResult && lastConsoleLog) {
720
+ scriptResult = lastConsoleLog;
721
+ }
722
+ }
723
+
724
+ // Determine and save programmatic output
725
+ const programmaticOutput = determineProgrammaticOutput(scriptResult, lastConsoleLog);
726
+ await ScriptRun.updateOne(
727
+ { _id: runId },
728
+ {
729
+ $set: {
730
+ programmaticOutput: programmaticOutput.programmaticOutput,
731
+ returnResult: scriptResult !== undefined && scriptResult !== null ? formatOutput(scriptResult) : '',
732
+ outputType: programmaticOutput.outputType
733
+ }
734
+ }
735
+ );
736
+
446
737
  return 0;
447
738
  } catch (err) {
448
739
  const baseMsg = err?.message || 'vm2 error';
@@ -0,0 +1,130 @@
1
+ const TelegramBot = require('node-telegram-bot-api');
2
+ const TelegramBotModel = require('../models/TelegramBot');
3
+ const Agent = require('../models/Agent');
4
+ const agentService = require('./agent.service');
5
+
6
+ const activeBots = new Map();
7
+
8
+ /**
9
+ * Start a telegram bot by its ID
10
+ */
11
+ async function startBot(botId) {
12
+ try {
13
+ const botDoc = await TelegramBotModel.findById(botId);
14
+ if (!botDoc) throw new Error('Bot not found');
15
+
16
+ if (activeBots.has(botId.toString())) {
17
+ await stopBot(botId);
18
+ }
19
+
20
+ const bot = new TelegramBot(botDoc.token, { polling: true });
21
+
22
+ bot.on('message', async (msg) => {
23
+ if (!msg.text) return;
24
+
25
+ // Security: check if user is allowed
26
+ if (botDoc.allowedUserIds && botDoc.allowedUserIds.length > 0) {
27
+ if (!botDoc.allowedUserIds.includes(msg.from.id.toString())) {
28
+ console.warn(`Unauthorized access attempt from ${msg.from.id} to bot ${botDoc.name}`);
29
+ return;
30
+ }
31
+ }
32
+
33
+ console.log(`[TelegramBot: ${botDoc.name}] Received: ${msg.text} from ${msg.from.id}`);
34
+
35
+ // Delegate to agent service
36
+ try {
37
+ if (msg.text.toLowerCase() === '/start') {
38
+ await bot.sendMessage(msg.chat.id, 'Hi there! I am your SuperBackend Agent. How can I help you today?');
39
+ return;
40
+ }
41
+
42
+ if (!botDoc.defaultAgentId) {
43
+ console.log(`[TelegramBot: ${botDoc.name}] No agent configured, ignoring message from ${msg.from.id}`);
44
+ return;
45
+ }
46
+
47
+ const response = await agentService.processMessage(botDoc.defaultAgentId, {
48
+ content: msg.text,
49
+ senderId: msg.from.id.toString(),
50
+ chatId: msg.chat.id.toString(),
51
+ metadata: {
52
+ firstName: msg.from.first_name,
53
+ lastName: msg.from.last_name,
54
+ username: msg.from.username
55
+ }
56
+ });
57
+
58
+ await bot.sendMessage(msg.chat.id, response.text || response);
59
+ } catch (err) {
60
+ console.error('Error processing message:', err);
61
+ await bot.sendMessage(msg.chat.id, 'Sorry, I encountered an error processing your request.');
62
+ }
63
+ });
64
+
65
+ bot.on('polling_error', (err) => {
66
+ console.error(`Telegram polling error [${botDoc.name}]:`, err);
67
+ });
68
+
69
+ activeBots.set(botId.toString(), bot);
70
+
71
+ botDoc.status = 'running';
72
+ botDoc.lastError = null;
73
+ await botDoc.save();
74
+
75
+ console.log(`Telegram bot [${botDoc.name}] started`);
76
+ return true;
77
+ } catch (err) {
78
+ console.error(`Failed to start Telegram bot [${botId}]:`, err);
79
+ const botDoc = await TelegramBotModel.findById(botId);
80
+ if (botDoc) {
81
+ botDoc.status = 'error';
82
+ botDoc.lastError = err.message;
83
+ await botDoc.save();
84
+ }
85
+ throw err;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Stop a telegram bot
91
+ */
92
+ async function stopBot(botId) {
93
+ const bot = activeBots.get(botId.toString());
94
+ if (bot) {
95
+ await bot.stopPolling();
96
+ activeBots.delete(botId.toString());
97
+ }
98
+
99
+ const botDoc = await TelegramBotModel.findById(botId);
100
+ if (botDoc) {
101
+ botDoc.status = 'stopped';
102
+ await botDoc.save();
103
+ }
104
+
105
+ console.log(`Telegram bot [${botId}] stopped`);
106
+ }
107
+
108
+ /**
109
+ * Initialize all active bots on startup
110
+ */
111
+ async function init() {
112
+ try {
113
+ const activeBotsDocs = await TelegramBotModel.find({ isActive: true });
114
+ for (const botDoc of activeBotsDocs) {
115
+ try {
116
+ await startBot(botDoc._id);
117
+ } catch (err) {
118
+ // Continue with other bots
119
+ }
120
+ }
121
+ } catch (err) {
122
+ console.error('Failed to initialize Telegram bots:', err);
123
+ }
124
+ }
125
+
126
+ module.exports = {
127
+ startBot,
128
+ stopBot,
129
+ init
130
+ };