@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.
- package/.env.example +10 -0
- package/index.js +2 -0
- package/manage.js +745 -0
- package/package.json +5 -2
- package/src/controllers/admin.controller.js +79 -6
- package/src/controllers/adminAgents.controller.js +37 -0
- package/src/controllers/adminExperiments.controller.js +200 -0
- package/src/controllers/adminLlm.controller.js +19 -0
- package/src/controllers/adminMarkdowns.controller.js +157 -0
- package/src/controllers/adminScripts.controller.js +243 -74
- package/src/controllers/adminTelegram.controller.js +72 -0
- package/src/controllers/experiments.controller.js +85 -0
- package/src/controllers/internalExperiments.controller.js +17 -0
- package/src/controllers/markdowns.controller.js +42 -0
- package/src/helpers/mongooseHelper.js +258 -0
- package/src/helpers/scriptBase.js +230 -0
- package/src/helpers/scriptRunner.js +335 -0
- package/src/middleware.js +195 -34
- package/src/models/Agent.js +105 -0
- package/src/models/AgentMessage.js +82 -0
- package/src/models/CacheEntry.js +1 -1
- package/src/models/ConsoleLog.js +1 -1
- package/src/models/Experiment.js +75 -0
- package/src/models/ExperimentAssignment.js +23 -0
- package/src/models/ExperimentEvent.js +26 -0
- package/src/models/ExperimentMetricBucket.js +30 -0
- package/src/models/GlobalSetting.js +1 -2
- package/src/models/Markdown.js +75 -0
- package/src/models/RateLimitCounter.js +1 -1
- package/src/models/ScriptDefinition.js +1 -0
- package/src/models/ScriptRun.js +8 -0
- package/src/models/TelegramBot.js +42 -0
- package/src/models/Webhook.js +2 -0
- package/src/routes/admin.routes.js +2 -0
- package/src/routes/adminAgents.routes.js +13 -0
- package/src/routes/adminConsoleManager.routes.js +1 -1
- package/src/routes/adminExperiments.routes.js +29 -0
- package/src/routes/adminLlm.routes.js +1 -0
- package/src/routes/adminMarkdowns.routes.js +16 -0
- package/src/routes/adminScripts.routes.js +4 -1
- package/src/routes/adminTelegram.routes.js +14 -0
- package/src/routes/blogInternal.routes.js +2 -2
- package/src/routes/experiments.routes.js +30 -0
- package/src/routes/internalExperiments.routes.js +15 -0
- package/src/routes/markdowns.routes.js +16 -0
- package/src/services/agent.service.js +546 -0
- package/src/services/agentHistory.service.js +345 -0
- package/src/services/agentTools.service.js +578 -0
- package/src/services/blogCronsBootstrap.service.js +7 -6
- package/src/services/consoleManager.service.js +56 -18
- package/src/services/consoleOverride.service.js +1 -0
- package/src/services/experiments.service.js +273 -0
- package/src/services/experimentsAggregation.service.js +308 -0
- package/src/services/experimentsCronsBootstrap.service.js +118 -0
- package/src/services/experimentsRetention.service.js +43 -0
- package/src/services/experimentsWs.service.js +134 -0
- package/src/services/globalSettings.service.js +15 -0
- package/src/services/jsonConfigs.service.js +24 -12
- package/src/services/llm.service.js +219 -6
- package/src/services/markdowns.service.js +522 -0
- package/src/services/scriptsRunner.service.js +514 -23
- package/src/services/telegram.service.js +130 -0
- package/src/utils/rbac/rightsRegistry.js +4 -0
- package/views/admin-agents.ejs +273 -0
- package/views/admin-coolify-deploy.ejs +8 -8
- package/views/admin-dashboard.ejs +63 -12
- package/views/admin-experiments.ejs +91 -0
- package/views/admin-markdowns.ejs +905 -0
- package/views/admin-scripts.ejs +817 -6
- package/views/admin-telegram.ejs +269 -0
- package/views/partials/dashboard/nav-items.ejs +4 -0
- package/views/partials/dashboard/palette.ejs +5 -3
- 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 };
|