1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
class CronModule {
constructor() {
this.commands = {};
this.terminal = null;
this.jobs = {};
this.counter = 0;
this.MIN_INTERVAL = 5;
this.STORAGE_KEY = 'webx11_cron_jobs';
this._restoreJobs();
}
requirements() {
return { met: true, missing: [] };
}
getHelpInfo() {
return {
'cron-every': 'cron-every <seconds> <command> — Schedule a command to run every N seconds',
'cron-list': 'cron-list — List all active cron jobs',
'cron-stop': 'cron-stop <id> — Stop a specific cron job',
'cron-clear': 'cron-clear — Stop all cron jobs'
};
}
registerCommands() {
this.commands['cron-every'] = (args) => this.every(args);
this.commands['cron-list'] = () => this.list();
this.commands['cron-stop'] = (args) => this.stop(args);
this.commands['cron-clear'] = () => this.clear();
}
_restoreJobs() {
try {
const saved = localStorage.getItem(this.STORAGE_KEY);
if (!saved) return;
const data = JSON.parse(saved);
if (!Array.isArray(data) || data.length === 0) return;
let maxId = 0;
data.forEach(j => {
if (j.id > maxId) maxId = j.id;
if (this.counter < j.id) this.counter = j.id;
this.jobs[j.id] = {
cmd: j.cmd,
interval: j.interval,
runs: j.runs || 0,
timer: setInterval(() => {
if (!this.jobs[j.id]) return;
this.jobs[j.id].runs++;
this.terminal.print(`[cron #${j.id}] Running: ${j.cmd}`, 'info');
this.terminal.executeCommand(j.cmd);
}, j.interval)
};
});
this.counter = maxId;
if (data.length > 0) this.terminal.print(`↻ Restored ${data.length} cron job(s) from storage`, 'info');
} catch (e) { /* silently ignore corrupt data */ }
}
_saveJobs() {
try {
const data = Object.entries(this.jobs).map(([id, j]) => ({
id: parseInt(id),
cmd: j.cmd,
interval: j.interval,
runs: j.runs
}));
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
} catch (e) { /* quota exceeded */ }
}
every(args) {
if (args.length < 2) return this.terminal.print('Usage: cron-every <seconds> <command>', 'warning');
const secs = parseInt(args[0]);
if (isNaN(secs) || secs < this.MIN_INTERVAL) return this.terminal.print(`Interval must be >= ${this.MIN_INTERVAL} seconds`, 'warning');
const cmd = args.slice(1).join(' ');
this.counter++;
const id = this.counter;
this.jobs[id] = {
cmd, interval: secs * 1000, runs: 0,
timer: setInterval(() => {
this.jobs[id].runs++;
this.terminal.print(`[cron #${id}] Running: ${cmd}`, 'info');
this.terminal.executeCommand(cmd);
}, secs * 1000)
};
this._saveJobs();
this.terminal.print(`Cron job #${id} started: "${cmd}" every ${secs}s`, 'success');
}
list() {
const ids = Object.keys(this.jobs);
if (ids.length === 0) return this.terminal.print('No active cron jobs', 'info');
this.terminal.print('=== Cron Jobs ===', 'info');
ids.forEach(id => {
const j = this.jobs[id];
this.terminal.print(` #${id}: "${j.cmd}" every ${j.interval / 1000}s (${j.runs} runs)`);
});
}
stop(args) {
if (!args[0]) return this.terminal.print('Usage: cron-stop <id>', 'warning');
const id = parseInt(args[0]);
if (!this.jobs[id]) return this.terminal.print(`Cron job #${id} not found`, 'error');
clearInterval(this.jobs[id].timer);
delete this.jobs[id];
this._saveJobs();
this.terminal.print(`Cron job #${id} stopped`, 'success');
}
clear() {
Object.keys(this.jobs).forEach(id => { clearInterval(this.jobs[id].timer); });
this.jobs = {};
localStorage.removeItem(this.STORAGE_KEY);
this.terminal.print('All cron jobs stopped', 'success');
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
webx11.moduleManager.registerModule(new CronModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/cron.js');
}
|