aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-leases.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-02 18:00:37 +0200
committerAgando <roduccomm@gmail.com>2026-06-02 18:00:37 +0200
commitaafb0fbde5289a45f9f818a00141f80b149be283 (patch)
treed4d8f588187719c99240fbaef1940c068402b892 /modules/core/module-leases.js
parent6d41e590a38c73ef60f1c04beaf8f5438f350f46 (diff)
auto: 2026-06-02_18:00 changes
Diffstat (limited to 'modules/core/module-leases.js')
-rw-r--r--modules/core/module-leases.js102
1 files changed, 102 insertions, 0 deletions
diff --git a/modules/core/module-leases.js b/modules/core/module-leases.js
new file mode 100644
index 0000000..b79870c
--- /dev/null
+++ b/modules/core/module-leases.js
@@ -0,0 +1,102 @@
+// module-leases.js — Lease system for agent task delegation
+// Commands: lease-create, lease-list, lease-revoke, lease-renew
+class LeaseModule {
+ constructor() {
+ this.commands = {};
+ this.terminal = null;
+ this._leases = {};
+ this._watchdogTimer = null;
+ }
+ registerCommands() {
+ this.commands['lease-create'] = (args) => this.createLease(args);
+ this.commands['lease-list'] = () => this.listLeases();
+ this.commands['lease-revoke'] = (args) => this.revokeLease(args[0]);
+ this.commands['lease-renew'] = (args) => this.renewLease(args[0], parseInt(args[1]) || 30);
+ }
+ init(terminal) {
+ this.terminal = terminal;
+ if (!this.terminal.fs.readFile('/leases/')) {
+ this.terminal.fs.mkdir('leases');
+ }
+ // Restore persisted leases
+ const files = this.terminal.fs.ls('/leases/');
+ if (files) {
+ files.forEach(f => {
+ if (f.name.endsWith('.json')) {
+ const data = this.terminal.fs.readFile(`/leases/${f.name}`);
+ if (data) {
+ try {
+ const lease = JSON.parse(data);
+ this._leases[lease.id] = lease;
+ } catch (e) {}
+ }
+ }
+ });
+ }
+ // Start watchdog every 5 seconds
+ this._watchdogTimer = setInterval(() => this._checkExpired(), 5000);
+ this.terminal.print('📜 Lease system loaded', 'info');
+ }
+ createLease(args) {
+ const workerId = args[1];
+ const duration = parseInt(args[2]) || 30;
+ const task = args.slice(3).join(' ') || 'generic task';
+ if (!workerId) return this.terminal.print('Usage: lease-create <agent-id> [duration_sec] [task]', 'error');
+
+ const lease = {
+ id: `lease-${Date.now()}`,
+ workerId,
+ orchestratorId: 'orchestrator-0',
+ task,
+ createdAt: new Date().toISOString(),
+ expiresAt: new Date(Date.now() + duration * 1000).toISOString(),
+ status: 'active',
+ duration
+ };
+ this._leases[lease.id] = lease;
+ this.terminal.fs.writeFile(`leases/${lease.id}.json`, JSON.stringify(lease));
+ this.terminal.print(`📜 Lease ${lease.id} created for ${workerId} (${duration}s, task: ${task.substring(0, 40)})`, 'success');
+ return lease;
+ }
+ listLeases() {
+ const all = Object.values(this._leases);
+ const active = all.filter(l => l.status === 'active');
+ const expired = all.filter(l => l.status === 'expired');
+ const revoked = all.filter(l => l.status === 'revoked');
+ this.terminal.print(`📜 Leases: ${active.length} active, ${expired.length} expired, ${revoked.length} revoked`, 'header');
+ active.forEach(l => {
+ const remaining = Math.max(0, Math.round((new Date(l.expiresAt) - Date.now()) / 1000));
+ this.terminal.print(` ${l.id} → ${l.workerId} (${remaining}s remain) — ${l.task.substring(0, 40)}`, 'info');
+ });
+ }
+ revokeLease(id) {
+ if (!this._leases[id]) return this.terminal.print(`❌ Lease ${id} not found`, 'error');
+ this._leases[id].status = 'revoked';
+ this._leases[id].revokedAt = new Date().toISOString();
+ this.terminal.fs.writeFile(`leases/${id}.json`, JSON.stringify(this._leases[id]));
+ this.terminal.print(`✅ Lease ${id} revoked`, 'warning');
+ }
+ renewLease(id, additionalSeconds) {
+ if (!this._leases[id] || this._leases[id].status !== 'active') {
+ return this.terminal.print(`❌ Lease ${id} not found or not active`, 'error');
+ }
+ const lease = this._leases[id];
+ const newExpiry = new Date(Date.now() + additionalSeconds * 1000);
+ lease.expiresAt = newExpiry.toISOString();
+ lease.duration += additionalSeconds;
+ lease.renewedAt = new Date().toISOString();
+ this.terminal.fs.writeFile(`leases/${id}.json`, JSON.stringify(lease));
+ this.terminal.print(`✅ Lease ${id} renewed (+${additionalSeconds}s)`, 'success');
+ }
+ _checkExpired() {
+ const now = Date.now();
+ Object.values(this._leases).forEach(lease => {
+ if (lease.status === 'active' && new Date(lease.expiresAt) < now) {
+ lease.status = 'expired';
+ lease.expiredAt = new Date().toISOString();
+ this.terminal.fs.writeFile(`leases/${lease.id}.json`, JSON.stringify(lease));
+ this.terminal.log('INFO', 'Lease', `Lease ${lease.id} for ${lease.workerId} expired`);
+ }
+ });
+ }
+} \ No newline at end of file