Przeglądaj źródła

Initial commit: Cron Manager plugin for scheduled task management

ShadowMan Service User 1 tydzień temu
commit
88e3a3ecd3
4 zmienionych plików z 248 dodań i 0 usunięć
  1. 21 0
      .gitignore
  2. 22 0
      .spkgignore
  3. 147 0
      index.js
  4. 58 0
      manifest.json

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+# Logs
+logs/
+*.log
+
+# Environment files
+.env
+.env.*
+
+# Package files
+*.spkg
+*.spkg.sig
+
+# Dependencies
+node_modules/
+
+# SDK (belongs to plugin-template, not plugin repos)
+shadowman-sdk.d.ts
+
+# OS files
+.DS_Store
+Thumbs.db

+ 22 - 0
.spkgignore

@@ -0,0 +1,22 @@
+# Dependencies
+node_modules/
+
+# Environment
+.env
+.env.*
+
+# Git
+.git/
+.gitignore
+
+# Package metadata
+.spkgignore
+
+# Test files
+test/
+tests/
+*.test.js
+*.spec.js
+
+# Logs
+*.log

+ 147 - 0
index.js

@@ -0,0 +1,147 @@
+
+/**
+ * Cron Manager Plugin v1.1.0
+ * Schedules delayed tasks and sends results back to the original conversation.
+ */
+
+var JOBS_KEY = 'cron_jobs';
+
+// Initialize state with jobs from storage (if any)
+var storedJobs = shadowman.storage.get(JOBS_KEY);
+if (!storedJobs) storedJobs = [];
+shadowman.state.set(JOBS_KEY, storedJobs);
+
+// --- Tools ---
+
+shadowman.tools.register('schedule_job', function(args) {
+    var schedule = args.schedule;
+    var instructions = args.instructions;
+    var conversationId = args.conversationId;
+    
+    var jobs = shadowman.state.get(JOBS_KEY) || [];
+    var jobId = 'job_' + Math.random().toString(36).substr(2, 9);
+    
+    var triggerTime = null;
+    var intervalMs = null;
+
+    // Parser: number = minutes interval, string = ISO timestamp
+    if (!isNaN(schedule)) {
+        intervalMs = parseInt(schedule) * 60 * 1000;
+    } else {
+        var date = new Date(schedule);
+        if (!isNaN(date.getTime())) {
+            triggerTime = date.getTime();
+        } else {
+            return { error: "Invalid schedule format. Use an ISO timestamp or a number of minutes for intervals." };
+        }
+    }
+
+    var job = {
+        id: jobId,
+        schedule: schedule,
+        instructions: instructions,
+        conversationId: conversationId,
+        triggerTime: triggerTime,
+        intervalMs: intervalMs,
+        lastRun: 0,
+        createdAt: Date.now()
+    };
+
+    jobs.push(job);
+    shadowman.storage.set(JOBS_KEY, jobs);
+    shadowman.state.set(JOBS_KEY, jobs);
+
+    return { 
+        status: "success", 
+        jobId: jobId, 
+        message: "Job scheduled. It will trigger at " + schedule + " and notify conversation " + conversationId + "."
+    };
+});
+
+shadowman.tools.register('list_jobs', function() {
+    var jobs = shadowman.state.get(JOBS_KEY) || [];
+    return { jobs: jobs };
+});
+
+shadowman.tools.register('cancel_job', function(args) {
+    var jobId = args.jobId;
+    var jobs = shadowman.state.get(JOBS_KEY) || [];
+    var filtered = [];
+    
+    for (var i = 0; i < jobs.length; i++) {
+        if (jobs[i].id !== jobId) {
+            filtered.push(jobs[i]);
+        }
+    }
+    
+    if (jobs.length === filtered.length) {
+        return { error: "Job not found." };
+    }
+    
+    shadowman.storage.set(JOBS_KEY, filtered);
+    shadowman.state.set(JOBS_KEY, filtered);
+    return { status: "success", message: "Job " + jobId + " cancelled." };
+});
+
+// --- Background Worker ---
+
+shadowman.background.run(function() {
+    var JOBS_KEY = 'cron_jobs';
+    
+    shadowman.log.info("Cron Manager worker started");
+
+    while (true) {
+        try {
+            var jobs = shadowman.state.get(JOBS_KEY);
+            
+            if (jobs && jobs.length > 0) {
+                var currentTime = Date.now();
+                var jobsToTrigger = [];
+                var jobsToRemove = [];
+
+                for (var i = 0; i < jobs.length; i++) {
+                    var job = jobs[i];
+                    if (job.triggerTime && currentTime >= job.triggerTime) {
+                        jobsToTrigger.push(job);
+                        jobsToRemove.push(job.id);
+                    } else if (job.intervalMs && (currentTime - job.lastRun) >= job.intervalMs) {
+                        jobsToTrigger.push(job);
+                    }
+                }
+
+                for (var i = 0; i < jobsToTrigger.length; i++) {
+                    var job = jobsToTrigger[i];
+                    shadowman.log.info("Triggering cron job " + job.id);
+                    
+                    shadowman.events.emit('message', {
+                        text: "⏰ **Cron Job Triggered**\n\n**Instructions:** " + job.instructions + "\n\n*This is an automated notification from Cron Manager.*",
+                        conversationId: job.conversationId,
+                        platform: 'CronManager',
+                        sender: 'Cron Manager'
+                    });
+
+                    if (job.intervalMs) {
+                        job.lastRun = currentTime;
+                    }
+                }
+
+                if (jobsToRemove.length > 0) {
+                    var remainingJobs = [];
+                    for (var i = 0; i < jobs.length; i++) {
+                        if (jobsToRemove.indexOf(jobs[i].id) === -1) {
+                            remainingJobs.push(jobs[i]);
+                        }
+                    }
+                    shadowman.state.set(JOBS_KEY, remainingJobs);
+                }
+            }
+
+            shadowman.utils.sleep(10000);
+        } catch (e) {
+            shadowman.log.error("Cron worker error: " + e.toString());
+            shadowman.utils.sleep(5000);
+        }
+    }
+});
+
+shadowman.log.info("Cron Manager plugin v1.1.0 ready");

+ 58 - 0
manifest.json

@@ -0,0 +1,58 @@
+{
+  "id": "cron-manager",
+  "name": "Cron Manager",
+  "description": "Schedules delayed tasks and sends results back to the original conversation.",
+  "version": "1.1.0",
+  "runtime": "quickjs",
+  "sdkVersion": 1,
+  "auto_start": true,
+  "tools": [
+    {
+      "name": "schedule_job",
+      "description": "Schedules a job to run at a specific time or interval. The result will be sent back to the specified conversation.",
+      "parameters": {
+        "type": "object",
+        "properties": {
+          "schedule": {
+            "type": "string",
+            "description": "The schedule. Can be an ISO 8601 timestamp (e.g., '2026-04-07T15:00:00Z') or an interval in minutes (e.g., '60' for every hour)."
+          },
+          "instructions": {
+            "type": "string",
+            "description": "The instructions or prompt to be sent back to the LLM when the job triggers."
+          },
+          "conversationId": {
+            "type": "string",
+            "description": "The ID of the conversation where the result should be posted."
+          }
+        },
+        "required": ["schedule", "instructions", "conversationId"]
+      }
+    },
+    {
+      "name": "list_jobs",
+      "description": "Lists all currently scheduled cron jobs.",
+      "parameters": {
+        "type": "object",
+        "properties": {}
+      }
+    },
+    {
+      "name": "cancel_job",
+      "description": "Cancels a scheduled job by its ID.",
+      "parameters": {
+        "type": "object",
+        "properties": {
+          "jobId": {
+            "type": "string",
+            "description": "The ID of the job to cancel."
+          }
+        },
+        "required": ["jobId"]
+      }
+    }
+  ],
+  "permissions": {
+    "allowStorage": true
+  }
+}