@intranefr/superbackend 1.5.1 → 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 (73) hide show
  1. package/.env.example +10 -0
  2. package/index.js +2 -0
  3. package/manage.js +745 -0
  4. package/package.json +5 -2
  5. package/src/controllers/admin.controller.js +79 -6
  6. package/src/controllers/adminAgents.controller.js +37 -0
  7. package/src/controllers/adminExperiments.controller.js +200 -0
  8. package/src/controllers/adminLlm.controller.js +19 -0
  9. package/src/controllers/adminMarkdowns.controller.js +157 -0
  10. package/src/controllers/adminScripts.controller.js +243 -74
  11. package/src/controllers/adminTelegram.controller.js +72 -0
  12. package/src/controllers/experiments.controller.js +85 -0
  13. package/src/controllers/internalExperiments.controller.js +17 -0
  14. package/src/controllers/markdowns.controller.js +42 -0
  15. package/src/helpers/mongooseHelper.js +258 -0
  16. package/src/helpers/scriptBase.js +230 -0
  17. package/src/helpers/scriptRunner.js +335 -0
  18. package/src/middleware.js +195 -34
  19. package/src/models/Agent.js +105 -0
  20. package/src/models/AgentMessage.js +82 -0
  21. package/src/models/CacheEntry.js +1 -1
  22. package/src/models/ConsoleLog.js +1 -1
  23. package/src/models/Experiment.js +75 -0
  24. package/src/models/ExperimentAssignment.js +23 -0
  25. package/src/models/ExperimentEvent.js +26 -0
  26. package/src/models/ExperimentMetricBucket.js +30 -0
  27. package/src/models/GlobalSetting.js +1 -2
  28. package/src/models/Markdown.js +75 -0
  29. package/src/models/RateLimitCounter.js +1 -1
  30. package/src/models/ScriptDefinition.js +1 -0
  31. package/src/models/ScriptRun.js +8 -0
  32. package/src/models/TelegramBot.js +42 -0
  33. package/src/models/Webhook.js +2 -0
  34. package/src/routes/admin.routes.js +2 -0
  35. package/src/routes/adminAgents.routes.js +13 -0
  36. package/src/routes/adminConsoleManager.routes.js +1 -1
  37. package/src/routes/adminExperiments.routes.js +29 -0
  38. package/src/routes/adminLlm.routes.js +1 -0
  39. package/src/routes/adminMarkdowns.routes.js +16 -0
  40. package/src/routes/adminScripts.routes.js +4 -1
  41. package/src/routes/adminTelegram.routes.js +14 -0
  42. package/src/routes/blogInternal.routes.js +2 -2
  43. package/src/routes/experiments.routes.js +30 -0
  44. package/src/routes/internalExperiments.routes.js +15 -0
  45. package/src/routes/markdowns.routes.js +16 -0
  46. package/src/services/agent.service.js +546 -0
  47. package/src/services/agentHistory.service.js +345 -0
  48. package/src/services/agentTools.service.js +578 -0
  49. package/src/services/blogCronsBootstrap.service.js +7 -6
  50. package/src/services/consoleManager.service.js +56 -18
  51. package/src/services/consoleOverride.service.js +1 -0
  52. package/src/services/experiments.service.js +273 -0
  53. package/src/services/experimentsAggregation.service.js +308 -0
  54. package/src/services/experimentsCronsBootstrap.service.js +118 -0
  55. package/src/services/experimentsRetention.service.js +43 -0
  56. package/src/services/experimentsWs.service.js +134 -0
  57. package/src/services/globalSettings.service.js +15 -0
  58. package/src/services/jsonConfigs.service.js +24 -12
  59. package/src/services/llm.service.js +219 -6
  60. package/src/services/markdowns.service.js +522 -0
  61. package/src/services/scriptsRunner.service.js +514 -23
  62. package/src/services/telegram.service.js +130 -0
  63. package/src/utils/rbac/rightsRegistry.js +4 -0
  64. package/views/admin-agents.ejs +273 -0
  65. package/views/admin-coolify-deploy.ejs +8 -8
  66. package/views/admin-dashboard.ejs +63 -12
  67. package/views/admin-experiments.ejs +91 -0
  68. package/views/admin-markdowns.ejs +905 -0
  69. package/views/admin-scripts.ejs +817 -6
  70. package/views/admin-telegram.ejs +269 -0
  71. package/views/partials/dashboard/nav-items.ejs +4 -0
  72. package/views/partials/dashboard/palette.ejs +5 -3
  73. package/src/middleware/internalCronAuth.js +0 -29
@@ -0,0 +1,335 @@
1
+ const { ScriptBase } = require('./scriptBase');
2
+
3
+ /**
4
+ * Utility for running scripts with proper error handling and cleanup
5
+ * Provides CLI wrapper functionality and execution management
6
+ */
7
+ class ScriptRunner {
8
+ /**
9
+ * Run a script class or function
10
+ * @param {Function|ScriptBase} ScriptClass - Script class or function
11
+ * @param {Object} options - Execution options
12
+ * @returns {Promise<any>} Script result
13
+ */
14
+ static async run(ScriptClass, options = {}) {
15
+ let script;
16
+
17
+ try {
18
+ // Handle different script types
19
+ script = ScriptRunner._createScriptInstance(ScriptClass, options);
20
+
21
+ // Validate script before running
22
+ const validation = script.validate();
23
+ if (!validation.valid) {
24
+ throw new Error(`Script validation failed: ${validation.errors.join(', ')}`);
25
+ }
26
+
27
+ // Log warnings if any
28
+ if (validation.warnings.length > 0) {
29
+ validation.warnings.forEach(warning => {
30
+ console.warn(`[ScriptRunner] Warning: ${warning}`);
31
+ });
32
+ }
33
+
34
+ return await script.run();
35
+ } catch (error) {
36
+ console.error('[ScriptRunner] Execution failed:', error.message);
37
+
38
+ if (script) {
39
+ await script.cleanup(script.context);
40
+ }
41
+
42
+ throw error;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Create script instance from various input types
48
+ * @private
49
+ * @param {Function|ScriptBase} ScriptClass - Script class or function
50
+ * @param {Object} options - Options for script creation
51
+ * @returns {ScriptBase} Script instance
52
+ */
53
+ static _createScriptInstance(ScriptClass, options = {}) {
54
+ // Handle plain async functions
55
+ if (typeof ScriptClass === 'function' && !ScriptBase.prototype.isPrototypeOf(ScriptClass.prototype)) {
56
+ // Wrap the function in a ScriptBase class
57
+ const WrappedScript = class extends ScriptBase {
58
+ async execute(context) {
59
+ return await ScriptClass(context, options);
60
+ }
61
+ };
62
+
63
+ return new WrappedScript(options);
64
+ }
65
+
66
+ // Handle ScriptBase classes
67
+ if (typeof ScriptClass === 'function' && ScriptBase.prototype.isPrototypeOf(ScriptClass.prototype)) {
68
+ return new ScriptClass(options);
69
+ }
70
+
71
+ // Handle ScriptBase instances
72
+ if (ScriptBase.prototype.isPrototypeOf(ScriptClass)) {
73
+ return ScriptClass;
74
+ }
75
+
76
+ throw new Error('Invalid script type: must be a function, ScriptBase class, or ScriptBase instance');
77
+ }
78
+
79
+ /**
80
+ * Create a CLI wrapper for scripts
81
+ * @param {Function|ScriptBase} ScriptClass - Script class or function
82
+ * @param {Object} defaultOptions - Default options
83
+ * @returns {Function} CLI-ready function
84
+ */
85
+ static createCli(ScriptClass, defaultOptions = {}) {
86
+ return async (options = {}) => {
87
+ const mergedOptions = { ...defaultOptions, ...options };
88
+
89
+ // Handle CLI execution
90
+ if (require.main === module) {
91
+ try {
92
+ // Parse command line arguments if provided
93
+ const cliOptions = ScriptRunner._parseCliArgs();
94
+ const finalOptions = { ...mergedOptions, ...cliOptions };
95
+
96
+ await ScriptRunner.run(ScriptClass, finalOptions);
97
+ console.log('✅ Script completed successfully');
98
+ process.exit(0);
99
+ } catch (error) {
100
+ console.error('❌ Script failed:', error.message);
101
+
102
+ // Show stack trace in debug mode
103
+ if (process.env.DEBUG || process.env.NODE_ENV === 'development') {
104
+ console.error(error.stack);
105
+ }
106
+
107
+ process.exit(1);
108
+ }
109
+ } else {
110
+ // Return result when required as module
111
+ return await ScriptRunner.run(ScriptClass, mergedOptions);
112
+ }
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Parse command line arguments
118
+ * @private
119
+ * @returns {Object} Parsed arguments
120
+ */
121
+ static _parseCliArgs() {
122
+ const args = process.argv.slice(2);
123
+ const options = {};
124
+
125
+ for (let i = 0; i < args.length; i++) {
126
+ const arg = args[i];
127
+
128
+ // Handle --key=value or --key value format
129
+ if (arg.startsWith('--')) {
130
+ const equalsIndex = arg.indexOf('=');
131
+
132
+ if (equalsIndex > 0) {
133
+ // --key=value format
134
+ const key = arg.substring(2, equalsIndex);
135
+ const value = arg.substring(equalsIndex + 1);
136
+ options[key] = ScriptRunner._parseValue(value);
137
+ } else {
138
+ // --key value format
139
+ const key = arg.substring(2);
140
+
141
+ if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
142
+ const value = args[i + 1];
143
+ options[key] = ScriptRunner._parseValue(value);
144
+ i++; // Skip next argument as it's a value
145
+ } else {
146
+ // Boolean flag
147
+ options[key] = true;
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ return options;
154
+ }
155
+
156
+ /**
157
+ * Parse string value to appropriate type
158
+ * @private
159
+ * @param {string} value - String value to parse
160
+ * @returns {any} Parsed value
161
+ */
162
+ static _parseValue(value) {
163
+ // Try parsing as JSON
164
+ try {
165
+ return JSON.parse(value);
166
+ } catch {
167
+ // Fallback to string
168
+ return value;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Create a batch script runner for multiple scripts
174
+ * @param {Array} scripts - Array of script configurations
175
+ * @param {Object} options - Batch options
176
+ * @returns {Function} Batch runner function
177
+ */
178
+ static createBatch(scripts, options = {}) {
179
+ const {
180
+ stopOnError = true,
181
+ parallel = false,
182
+ maxConcurrency = 3
183
+ } = options;
184
+
185
+ return async (runOptions = {}) => {
186
+ const results = [];
187
+ const errors = [];
188
+
189
+ console.log(`[BatchRunner] Running ${scripts.length} scripts (${parallel ? 'parallel' : 'sequential'})`);
190
+
191
+ if (parallel) {
192
+ // Run scripts in parallel with concurrency limit
193
+ const chunks = ScriptRunner._chunkArray(scripts, maxConcurrency);
194
+
195
+ for (const chunk of chunks) {
196
+ const chunkPromises = chunk.map(async (scriptConfig, index) => {
197
+ try {
198
+ const { script, options: scriptOptions } = scriptConfig;
199
+ const result = await ScriptRunner.run(script, { ...runOptions, ...scriptOptions });
200
+ return { index, result, error: null };
201
+ } catch (error) {
202
+ return { index, result: null, error };
203
+ }
204
+ });
205
+
206
+ const chunkResults = await Promise.all(chunkPromises);
207
+
208
+ for (const { index, result, error } of chunkResults) {
209
+ if (error) {
210
+ errors.push({ index, error });
211
+ if (stopOnError) {
212
+ throw error;
213
+ }
214
+ } else {
215
+ results[index] = result;
216
+ }
217
+ }
218
+ }
219
+ } else {
220
+ // Run scripts sequentially
221
+ for (let i = 0; i < scripts.length; i++) {
222
+ const { script, options: scriptOptions } = scripts[i];
223
+
224
+ try {
225
+ console.log(`[BatchRunner] Running script ${i + 1}/${scripts.length}`);
226
+ const result = await ScriptRunner.run(script, { ...runOptions, ...scriptOptions });
227
+ results.push(result);
228
+ } catch (error) {
229
+ errors.push({ index: i, error });
230
+ if (stopOnError) {
231
+ throw error;
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ return { results, errors };
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Split array into chunks
243
+ * @private
244
+ * @param {Array} array - Array to split
245
+ * @param {number} size - Chunk size
246
+ * @returns {Array} Array of chunks
247
+ */
248
+ static _chunkArray(array, size) {
249
+ const chunks = [];
250
+ for (let i = 0; i < array.length; i += size) {
251
+ chunks.push(array.slice(i, i + size));
252
+ }
253
+ return chunks;
254
+ }
255
+
256
+ /**
257
+ * Create a scheduled script runner
258
+ * @param {Function|ScriptBase} ScriptClass - Script to run
259
+ * @param {Object} schedule - Schedule configuration
260
+ * @returns {Object} Scheduled runner
261
+ */
262
+ static createScheduled(ScriptClass, schedule = {}) {
263
+ const {
264
+ interval,
265
+ cron,
266
+ maxRuns = null,
267
+ runOnStart = false
268
+ } = schedule;
269
+
270
+ let runCount = 0;
271
+ let timer = null;
272
+
273
+ const scheduledRunner = {
274
+ isRunning: false,
275
+ start() {
276
+ if (this.isRunning) {
277
+ throw new Error('Scheduled runner is already running');
278
+ }
279
+
280
+ this.isRunning = true;
281
+
282
+ if (runOnStart) {
283
+ this.runScript();
284
+ }
285
+
286
+ if (interval) {
287
+ timer = setInterval(() => this.runScript(), interval);
288
+ } else if (cron) {
289
+ // Note: Would need a cron library implementation
290
+ throw new Error('Cron scheduling not implemented yet');
291
+ } else {
292
+ throw new Error('Must specify either interval or cron schedule');
293
+ }
294
+ },
295
+
296
+ stop() {
297
+ if (timer) {
298
+ clearInterval(timer);
299
+ timer = null;
300
+ }
301
+ this.isRunning = false;
302
+ },
303
+
304
+ async runScript() {
305
+ if (maxRuns && runCount >= maxRuns) {
306
+ console.log('[ScheduledRunner] Maximum runs reached, stopping');
307
+ this.stop();
308
+ return;
309
+ }
310
+
311
+ try {
312
+ runCount++;
313
+ console.log(`[ScheduledRunner] Run #${runCount}`);
314
+ await ScriptRunner.run(ScriptClass);
315
+ } catch (error) {
316
+ console.error('[ScheduledRunner] Scheduled run failed:', error);
317
+ }
318
+ },
319
+
320
+ getStatus() {
321
+ return {
322
+ isRunning: this.isRunning,
323
+ runCount,
324
+ maxRuns,
325
+ interval,
326
+ cron
327
+ };
328
+ }
329
+ };
330
+
331
+ return scheduledRunner;
332
+ }
333
+ }
334
+
335
+ module.exports = { ScriptRunner };