ideaco 1.1.5

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 (159) hide show
  1. package/.dockerignore +33 -0
  2. package/.nvmrc +1 -0
  3. package/ARCHITECTURE.md +394 -0
  4. package/Dockerfile +50 -0
  5. package/LICENSE +29 -0
  6. package/README.md +206 -0
  7. package/bin/i18n.js +46 -0
  8. package/bin/ideaco.js +494 -0
  9. package/deploy.sh +15 -0
  10. package/docker-compose.yml +30 -0
  11. package/electron/main.cjs +986 -0
  12. package/electron/preload.cjs +14 -0
  13. package/electron/web-backends.cjs +854 -0
  14. package/jsconfig.json +8 -0
  15. package/next.config.mjs +34 -0
  16. package/package.json +134 -0
  17. package/postcss.config.mjs +6 -0
  18. package/public/demo/dashboard.png +0 -0
  19. package/public/demo/employee.png +0 -0
  20. package/public/demo/messages.png +0 -0
  21. package/public/demo/office.png +0 -0
  22. package/public/demo/requirement.png +0 -0
  23. package/public/logo.jpeg +0 -0
  24. package/public/logo.png +0 -0
  25. package/scripts/prepare-electron.js +67 -0
  26. package/scripts/release.js +76 -0
  27. package/src/app/api/agents/[agentId]/chat/route.js +70 -0
  28. package/src/app/api/agents/[agentId]/conversations/route.js +35 -0
  29. package/src/app/api/agents/[agentId]/route.js +106 -0
  30. package/src/app/api/avatar/route.js +104 -0
  31. package/src/app/api/browse-dir/route.js +44 -0
  32. package/src/app/api/chat/route.js +265 -0
  33. package/src/app/api/company/factory-reset/route.js +43 -0
  34. package/src/app/api/company/route.js +82 -0
  35. package/src/app/api/departments/[deptId]/agents/[agentId]/dismiss/route.js +19 -0
  36. package/src/app/api/departments/route.js +92 -0
  37. package/src/app/api/group-chat-loop/events/route.js +70 -0
  38. package/src/app/api/group-chat-loop/route.js +94 -0
  39. package/src/app/api/mailbox/route.js +100 -0
  40. package/src/app/api/messages/route.js +14 -0
  41. package/src/app/api/providers/[id]/configure/route.js +21 -0
  42. package/src/app/api/providers/[id]/refresh-cookie/route.js +38 -0
  43. package/src/app/api/providers/[id]/test-cookie/route.js +28 -0
  44. package/src/app/api/providers/route.js +11 -0
  45. package/src/app/api/requirements/route.js +242 -0
  46. package/src/app/api/secretary/route.js +65 -0
  47. package/src/app/api/system/cli-backends/route.js +91 -0
  48. package/src/app/api/system/cron/route.js +110 -0
  49. package/src/app/api/system/knowledge/route.js +104 -0
  50. package/src/app/api/system/plugins/route.js +40 -0
  51. package/src/app/api/system/skills/route.js +46 -0
  52. package/src/app/api/system/status/route.js +46 -0
  53. package/src/app/api/talent-market/[profileId]/recall/route.js +22 -0
  54. package/src/app/api/talent-market/[profileId]/route.js +17 -0
  55. package/src/app/api/talent-market/route.js +26 -0
  56. package/src/app/api/teams/route.js +773 -0
  57. package/src/app/api/ws-files/[departmentId]/file/route.js +27 -0
  58. package/src/app/api/ws-files/[departmentId]/files/route.js +22 -0
  59. package/src/app/globals.css +130 -0
  60. package/src/app/layout.jsx +40 -0
  61. package/src/app/page.jsx +97 -0
  62. package/src/components/AgentChatModal.jsx +164 -0
  63. package/src/components/AgentDetailModal.jsx +425 -0
  64. package/src/components/AgentSpyModal.jsx +481 -0
  65. package/src/components/AvatarGrid.jsx +29 -0
  66. package/src/components/BossProfileModal.jsx +162 -0
  67. package/src/components/CachedAvatar.jsx +77 -0
  68. package/src/components/ChatPanel.jsx +219 -0
  69. package/src/components/ChatShared.jsx +255 -0
  70. package/src/components/DepartmentDetail.jsx +842 -0
  71. package/src/components/DepartmentView.jsx +367 -0
  72. package/src/components/FileReference.jsx +260 -0
  73. package/src/components/FilesView.jsx +465 -0
  74. package/src/components/GroupChatView.jsx +799 -0
  75. package/src/components/Mailbox.jsx +926 -0
  76. package/src/components/MessagesView.jsx +112 -0
  77. package/src/components/OnboardingGuide.jsx +209 -0
  78. package/src/components/OrgTree.jsx +151 -0
  79. package/src/components/Overview.jsx +391 -0
  80. package/src/components/PixelOffice.jsx +2281 -0
  81. package/src/components/ProviderGrid.jsx +551 -0
  82. package/src/components/ProvidersBoard.jsx +16 -0
  83. package/src/components/RequirementDetail.jsx +1279 -0
  84. package/src/components/RequirementsBoard.jsx +187 -0
  85. package/src/components/SecretarySettings.jsx +295 -0
  86. package/src/components/SetupWizard.jsx +388 -0
  87. package/src/components/Sidebar.jsx +169 -0
  88. package/src/components/SystemMonitor.jsx +808 -0
  89. package/src/components/TalentMarket.jsx +183 -0
  90. package/src/components/TeamDetail.jsx +697 -0
  91. package/src/core/agent/base-agent.js +104 -0
  92. package/src/core/agent/chat-store.js +602 -0
  93. package/src/core/agent/cli-agent/backends/claude-code/README.md +52 -0
  94. package/src/core/agent/cli-agent/backends/claude-code/config.js +27 -0
  95. package/src/core/agent/cli-agent/backends/codebuddy/README.md +236 -0
  96. package/src/core/agent/cli-agent/backends/codebuddy/config.js +27 -0
  97. package/src/core/agent/cli-agent/backends/codex/README.md +51 -0
  98. package/src/core/agent/cli-agent/backends/codex/config.js +27 -0
  99. package/src/core/agent/cli-agent/backends/index.js +27 -0
  100. package/src/core/agent/cli-agent/backends/registry.js +580 -0
  101. package/src/core/agent/cli-agent/index.js +154 -0
  102. package/src/core/agent/index.js +60 -0
  103. package/src/core/agent/llm-agent/client.js +320 -0
  104. package/src/core/agent/llm-agent/index.js +97 -0
  105. package/src/core/agent/message-bus.js +211 -0
  106. package/src/core/agent/session.js +608 -0
  107. package/src/core/agent/tools.js +596 -0
  108. package/src/core/agent/web-agent/backends/base-backend.js +180 -0
  109. package/src/core/agent/web-agent/backends/chatgpt/client.js +146 -0
  110. package/src/core/agent/web-agent/backends/chatgpt/config.js +148 -0
  111. package/src/core/agent/web-agent/backends/chatgpt/dom-scripts.js +303 -0
  112. package/src/core/agent/web-agent/backends/index.js +91 -0
  113. package/src/core/agent/web-agent/index.js +278 -0
  114. package/src/core/agent/web-agent/web-client.js +407 -0
  115. package/src/core/employee/base-employee.js +1088 -0
  116. package/src/core/employee/index.js +35 -0
  117. package/src/core/employee/knowledge.js +327 -0
  118. package/src/core/employee/lifecycle.js +990 -0
  119. package/src/core/employee/memory/index.js +642 -0
  120. package/src/core/employee/memory/store.js +143 -0
  121. package/src/core/employee/performance.js +224 -0
  122. package/src/core/employee/secretary.js +625 -0
  123. package/src/core/employee/skills.js +398 -0
  124. package/src/core/index.js +38 -0
  125. package/src/core/organization/company.js +2600 -0
  126. package/src/core/organization/department.js +737 -0
  127. package/src/core/organization/group-chat-loop.js +264 -0
  128. package/src/core/organization/index.js +8 -0
  129. package/src/core/organization/persistence.js +111 -0
  130. package/src/core/organization/team.js +267 -0
  131. package/src/core/organization/workforce/hr.js +377 -0
  132. package/src/core/organization/workforce/providers.js +468 -0
  133. package/src/core/organization/workforce/role-archetypes.js +805 -0
  134. package/src/core/organization/workforce/talent-market.js +205 -0
  135. package/src/core/prompts.js +532 -0
  136. package/src/core/requirement.js +1789 -0
  137. package/src/core/system/audit.js +483 -0
  138. package/src/core/system/cron.js +449 -0
  139. package/src/core/system/index.js +7 -0
  140. package/src/core/system/plugin.js +2183 -0
  141. package/src/core/utils/json-parse.js +188 -0
  142. package/src/core/workspace.js +239 -0
  143. package/src/lib/api-i18n.js +211 -0
  144. package/src/lib/avatar.js +268 -0
  145. package/src/lib/client-store.js +1025 -0
  146. package/src/lib/config-validator.js +483 -0
  147. package/src/lib/format-time.js +22 -0
  148. package/src/lib/hooks.js +414 -0
  149. package/src/lib/i18n.js +134 -0
  150. package/src/lib/paths.js +23 -0
  151. package/src/lib/store.js +72 -0
  152. package/src/locales/de.js +393 -0
  153. package/src/locales/en.js +1054 -0
  154. package/src/locales/es.js +393 -0
  155. package/src/locales/fr.js +393 -0
  156. package/src/locales/ja.js +501 -0
  157. package/src/locales/ko.js +513 -0
  158. package/src/locales/zh.js +828 -0
  159. package/tailwind.config.mjs +11 -0
@@ -0,0 +1,449 @@
1
+ /**
2
+ * Cron Scheduler - Automated task scheduling system for agents
3
+ *
4
+ * Distilled from OpenClaw's cron system (vendor/openclaw/src/cron/)
5
+ * Re-implemented as an enterprise "automated workflow / standing orders" system
6
+ *
7
+ * Features:
8
+ * - Cron expression parsing (simplified subset)
9
+ * - Job registration and lifecycle management
10
+ * - Agent-bound scheduled tasks
11
+ * - Execution history and failure tracking
12
+ * - Graceful concurrent execution control
13
+ */
14
+ import { v4 as uuidv4 } from 'uuid';
15
+
16
+ /**
17
+ * Job status enum
18
+ */
19
+ export const JobStatus = {
20
+ ACTIVE: 'active',
21
+ PAUSED: 'paused',
22
+ RUNNING: 'running',
23
+ COMPLETED: 'completed', // For one-shot jobs
24
+ ERROR: 'error',
25
+ };
26
+
27
+ /**
28
+ * Simplified cron expression parser
29
+ * Supports: "every Xm" (minutes), "every Xh" (hours), "daily HH:MM", "weekly DOW HH:MM"
30
+ * Also supports standard 5-field cron: "* * * * *" (min hour dom month dow)
31
+ */
32
+ export function parseCronExpression(expression) {
33
+ const expr = expression.trim().toLowerCase();
34
+
35
+ // "every Xm" - every X minutes
36
+ const minuteMatch = expr.match(/^every\s+(\d+)\s*m(?:in(?:ute)?s?)?$/);
37
+ if (minuteMatch) {
38
+ return { type: 'interval', intervalMs: parseInt(minuteMatch[1]) * 60 * 1000 };
39
+ }
40
+
41
+ // "every Xh" - every X hours
42
+ const hourMatch = expr.match(/^every\s+(\d+)\s*h(?:ours?)?$/);
43
+ if (hourMatch) {
44
+ return { type: 'interval', intervalMs: parseInt(hourMatch[1]) * 3600 * 1000 };
45
+ }
46
+
47
+ // "daily HH:MM" - daily at specific time
48
+ const dailyMatch = expr.match(/^daily\s+(\d{1,2}):(\d{2})$/);
49
+ if (dailyMatch) {
50
+ return {
51
+ type: 'daily',
52
+ hour: parseInt(dailyMatch[1]),
53
+ minute: parseInt(dailyMatch[2]),
54
+ };
55
+ }
56
+
57
+ // "weekly DOW HH:MM" - weekly on specific day and time
58
+ const weeklyMatch = expr.match(/^weekly\s+(mon|tue|wed|thu|fri|sat|sun)\s+(\d{1,2}):(\d{2})$/);
59
+ if (weeklyMatch) {
60
+ const dayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
61
+ return {
62
+ type: 'weekly',
63
+ dayOfWeek: dayMap[weeklyMatch[1]],
64
+ hour: parseInt(weeklyMatch[2]),
65
+ minute: parseInt(weeklyMatch[3]),
66
+ };
67
+ }
68
+
69
+ throw new Error(`Invalid cron expression: "${expression}". Supported formats: "every Xm", "every Xh", "daily HH:MM", "weekly DOW HH:MM"`);
70
+ }
71
+
72
+ /**
73
+ * Calculate next run time for a parsed cron schedule
74
+ * @param {object} schedule - Parsed cron schedule
75
+ * @param {Date} after - Calculate next run after this time
76
+ * @returns {Date}
77
+ */
78
+ export function getNextRunTime(schedule, after = new Date()) {
79
+ if (schedule.type === 'interval') {
80
+ return new Date(after.getTime() + schedule.intervalMs);
81
+ }
82
+
83
+ if (schedule.type === 'daily') {
84
+ const next = new Date(after);
85
+ next.setHours(schedule.hour, schedule.minute, 0, 0);
86
+ if (next <= after) {
87
+ next.setDate(next.getDate() + 1);
88
+ }
89
+ return next;
90
+ }
91
+
92
+ if (schedule.type === 'weekly') {
93
+ const next = new Date(after);
94
+ next.setHours(schedule.hour, schedule.minute, 0, 0);
95
+ const currentDay = next.getDay();
96
+ let daysUntil = schedule.dayOfWeek - currentDay;
97
+ if (daysUntil < 0 || (daysUntil === 0 && next <= after)) {
98
+ daysUntil += 7;
99
+ }
100
+ next.setDate(next.getDate() + daysUntil);
101
+ return next;
102
+ }
103
+
104
+ throw new Error('Unknown schedule type');
105
+ }
106
+
107
+ /**
108
+ * Scheduled Job definition
109
+ */
110
+ class CronJob {
111
+ /**
112
+ * @param {object} config
113
+ * @param {string} config.id - Unique job ID
114
+ * @param {string} config.name - Human-readable job name
115
+ * @param {string} config.description - What this job does
116
+ * @param {string} config.cronExpression - Schedule expression
117
+ * @param {string} config.agentId - Agent to execute this job
118
+ * @param {string} config.taskPrompt - The prompt/instruction for the agent
119
+ * @param {boolean} config.oneShot - If true, run once then mark completed
120
+ * @param {number} config.maxConsecutiveFailures - Stop after N consecutive failures
121
+ */
122
+ constructor(config) {
123
+ this.id = config.id || uuidv4();
124
+ this.name = config.name;
125
+ this.description = config.description || '';
126
+ this.cronExpression = config.cronExpression;
127
+ this.schedule = parseCronExpression(config.cronExpression);
128
+ this.agentId = config.agentId;
129
+ this.taskPrompt = config.taskPrompt;
130
+ this.oneShot = config.oneShot || false;
131
+ this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? 3;
132
+
133
+ this.status = JobStatus.ACTIVE;
134
+ this.nextRun = getNextRunTime(this.schedule);
135
+ this.lastRun = null;
136
+ this.lastResult = null;
137
+ this.lastError = null;
138
+ this.runCount = 0;
139
+ this.failCount = 0;
140
+ this.consecutiveFailures = 0;
141
+ this.createdAt = new Date();
142
+
143
+ // History of recent executions
144
+ this.history = [];
145
+ this.maxHistory = 20;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Cron Scheduler - Manages and executes scheduled jobs
151
+ */
152
+ export class CronScheduler {
153
+ /**
154
+ * @param {object} options
155
+ * @param {number} options.tickInterval - How often to check for jobs to run (ms)
156
+ * @param {Function} options.executor - async (agentId, taskPrompt, jobId) => result
157
+ * @param {Function} options.onJobRun - Callback when a job starts running
158
+ * @param {Function} options.onJobComplete - Callback when a job completes
159
+ * @param {Function} options.onJobError - Callback when a job fails
160
+ */
161
+ constructor(options = {}) {
162
+ this.tickInterval = options.tickInterval ?? 60000; // Check every minute
163
+ this.executor = options.executor || null;
164
+ this.onJobRun = options.onJobRun || null;
165
+ this.onJobComplete = options.onJobComplete || null;
166
+ this.onJobError = options.onJobError || null;
167
+
168
+ this.jobs = new Map(); // id -> CronJob
169
+ this.timer = null;
170
+ this.running = false;
171
+ }
172
+
173
+ /**
174
+ * Register a new scheduled job
175
+ * @param {object} config - Job configuration
176
+ * @returns {CronJob}
177
+ */
178
+ addJob(config) {
179
+ const job = new CronJob(config);
180
+ this.jobs.set(job.id, job);
181
+ console.log(`⏰ Cron job registered: "${job.name}" [${job.cronExpression}]`);
182
+ return job;
183
+ }
184
+
185
+ /**
186
+ * Remove a job
187
+ * @param {string} jobId
188
+ */
189
+ removeJob(jobId) {
190
+ this.jobs.delete(jobId);
191
+ }
192
+
193
+ /**
194
+ * Pause a job
195
+ * @param {string} jobId
196
+ */
197
+ pauseJob(jobId) {
198
+ const job = this.jobs.get(jobId);
199
+ if (job) job.status = JobStatus.PAUSED;
200
+ }
201
+
202
+ /**
203
+ * Resume a paused job
204
+ * @param {string} jobId
205
+ */
206
+ resumeJob(jobId) {
207
+ const job = this.jobs.get(jobId);
208
+ if (job && (job.status === JobStatus.PAUSED || job.status === JobStatus.ERROR)) {
209
+ job.status = JobStatus.ACTIVE;
210
+ job.consecutiveFailures = 0;
211
+ job.nextRun = getNextRunTime(job.schedule);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Start the scheduler
217
+ */
218
+ start() {
219
+ if (this.running) return;
220
+ this.running = true;
221
+ console.log(`⏰ Cron scheduler started (tick every ${this.tickInterval / 1000}s)`);
222
+ this.timer = setInterval(() => this._tick(), this.tickInterval);
223
+ // Run an immediate tick
224
+ this._tick();
225
+ }
226
+
227
+ /**
228
+ * Stop the scheduler
229
+ */
230
+ stop() {
231
+ this.running = false;
232
+ if (this.timer) {
233
+ clearInterval(this.timer);
234
+ this.timer = null;
235
+ }
236
+ console.log('⏰ Cron scheduler stopped');
237
+ }
238
+
239
+ /**
240
+ * Check for and execute due jobs
241
+ */
242
+ async _tick() {
243
+ const now = new Date();
244
+
245
+ for (const [jobId, job] of this.jobs) {
246
+ // Skip non-active jobs
247
+ if (job.status !== JobStatus.ACTIVE) continue;
248
+
249
+ // Check if job is due
250
+ if (job.nextRun && job.nextRun <= now) {
251
+ await this._executeJob(job);
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Execute a single job
258
+ * @param {CronJob} job
259
+ */
260
+ async _executeJob(job) {
261
+ if (!this.executor) {
262
+ console.warn(`[CronScheduler] No executor configured, skipping job: ${job.name}`);
263
+ return;
264
+ }
265
+
266
+ job.status = JobStatus.RUNNING;
267
+ job.lastRun = new Date();
268
+ job.runCount++;
269
+
270
+ // Notify: job starting
271
+ if (this.onJobRun) {
272
+ try { this.onJobRun(job); } catch {}
273
+ }
274
+
275
+ const historyEntry = {
276
+ runAt: job.lastRun.toISOString(),
277
+ success: false,
278
+ result: null,
279
+ error: null,
280
+ duration: 0,
281
+ };
282
+
283
+ const startTime = Date.now();
284
+
285
+ try {
286
+ const result = await this.executor(job.agentId, job.taskPrompt, job.id);
287
+ const duration = Date.now() - startTime;
288
+
289
+ job.lastResult = result;
290
+ job.lastError = null;
291
+ job.consecutiveFailures = 0;
292
+
293
+ historyEntry.success = true;
294
+ historyEntry.result = typeof result === 'string' ? result.slice(0, 200) : 'OK';
295
+ historyEntry.duration = duration;
296
+
297
+ // Handle one-shot jobs
298
+ if (job.oneShot) {
299
+ job.status = JobStatus.COMPLETED;
300
+ } else {
301
+ job.status = JobStatus.ACTIVE;
302
+ job.nextRun = getNextRunTime(job.schedule);
303
+ }
304
+
305
+ // Notify: job completed
306
+ if (this.onJobComplete) {
307
+ try { this.onJobComplete(job, result); } catch {}
308
+ }
309
+
310
+ } catch (error) {
311
+ const duration = Date.now() - startTime;
312
+
313
+ job.lastError = error.message;
314
+ job.failCount++;
315
+ job.consecutiveFailures++;
316
+
317
+ historyEntry.error = error.message;
318
+ historyEntry.duration = duration;
319
+
320
+ // Check if we should stop the job
321
+ if (job.consecutiveFailures >= job.maxConsecutiveFailures) {
322
+ job.status = JobStatus.ERROR;
323
+ console.error(`⏰ Cron job "${job.name}" stopped after ${job.consecutiveFailures} consecutive failures`);
324
+ } else {
325
+ job.status = JobStatus.ACTIVE;
326
+ job.nextRun = getNextRunTime(job.schedule);
327
+ }
328
+
329
+ // Notify: job error
330
+ if (this.onJobError) {
331
+ try { this.onJobError(job, error); } catch {}
332
+ }
333
+ }
334
+
335
+ // Add to history
336
+ job.history.push(historyEntry);
337
+ if (job.history.length > job.maxHistory) {
338
+ job.history.shift();
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Manually trigger a job (ignoring schedule)
344
+ * @param {string} jobId
345
+ * @returns {Promise}
346
+ */
347
+ async triggerJob(jobId) {
348
+ const job = this.jobs.get(jobId);
349
+ if (!job) throw new Error(`Job not found: ${jobId}`);
350
+ return this._executeJob(job);
351
+ }
352
+
353
+ /**
354
+ * List all jobs with their status
355
+ * @returns {Array}
356
+ */
357
+ listJobs() {
358
+ return [...this.jobs.values()].map(job => ({
359
+ id: job.id,
360
+ name: job.name,
361
+ description: job.description,
362
+ cronExpression: job.cronExpression,
363
+ agentId: job.agentId,
364
+ status: job.status,
365
+ nextRun: job.nextRun?.toISOString() || null,
366
+ lastRun: job.lastRun?.toISOString() || null,
367
+ lastError: job.lastError,
368
+ runCount: job.runCount,
369
+ failCount: job.failCount,
370
+ consecutiveFailures: job.consecutiveFailures,
371
+ historyLength: job.history.length,
372
+ }));
373
+ }
374
+
375
+ /**
376
+ * Get execution history for a specific job
377
+ * @param {string} jobId
378
+ * @returns {Array}
379
+ */
380
+ getJobHistory(jobId) {
381
+ const job = this.jobs.get(jobId);
382
+ if (!job) return [];
383
+ return [...job.history].reverse();
384
+ }
385
+
386
+ /**
387
+ * Get scheduler summary
388
+ * @returns {object}
389
+ */
390
+ getSummary() {
391
+ const jobs = [...this.jobs.values()];
392
+ return {
393
+ running: this.running,
394
+ totalJobs: jobs.length,
395
+ activeJobs: jobs.filter(j => j.status === JobStatus.ACTIVE).length,
396
+ pausedJobs: jobs.filter(j => j.status === JobStatus.PAUSED).length,
397
+ errorJobs: jobs.filter(j => j.status === JobStatus.ERROR).length,
398
+ totalRuns: jobs.reduce((sum, j) => sum + j.runCount, 0),
399
+ totalFailures: jobs.reduce((sum, j) => sum + j.failCount, 0),
400
+ };
401
+ }
402
+
403
+ /**
404
+ * Serialize scheduler state (for persistence)
405
+ * @returns {object}
406
+ */
407
+ serialize() {
408
+ const jobs = [];
409
+ for (const [id, job] of this.jobs) {
410
+ jobs.push({
411
+ id: job.id,
412
+ name: job.name,
413
+ description: job.description,
414
+ cronExpression: job.cronExpression,
415
+ agentId: job.agentId,
416
+ taskPrompt: job.taskPrompt,
417
+ oneShot: job.oneShot,
418
+ maxConsecutiveFailures: job.maxConsecutiveFailures,
419
+ status: job.status,
420
+ runCount: job.runCount,
421
+ failCount: job.failCount,
422
+ createdAt: job.createdAt.toISOString(),
423
+ });
424
+ }
425
+ return { jobs };
426
+ }
427
+
428
+ /**
429
+ * Restore scheduler state from serialized data
430
+ * @param {object} data
431
+ */
432
+ restore(data) {
433
+ if (!data || !data.jobs) return;
434
+ for (const jobData of data.jobs) {
435
+ try {
436
+ const job = this.addJob(jobData);
437
+ if (jobData.status === JobStatus.PAUSED) job.status = JobStatus.PAUSED;
438
+ if (jobData.status === JobStatus.ERROR) job.status = JobStatus.ERROR;
439
+ job.runCount = jobData.runCount || 0;
440
+ job.failCount = jobData.failCount || 0;
441
+ } catch (err) {
442
+ console.error(`[CronScheduler] Failed to restore job "${jobData.name}":`, err.message);
443
+ }
444
+ }
445
+ }
446
+ }
447
+
448
+ // Global singleton
449
+ export const cronScheduler = new CronScheduler();
@@ -0,0 +1,7 @@
1
+ /**
2
+ * System Module - Infrastructure services
3
+ * Contains Cron scheduling, Plugin system, Security audit
4
+ */
5
+ export { AuditLogger, SecurityGuard, auditLogger, securityGuard, AuditLevel, AuditCategory } from './audit.js';
6
+ export { PluginRegistry, pluginRegistry, PluginManifest, HookPoint, PluginState, initPluginRuntime } from './plugin.js';
7
+ export { CronScheduler, cronScheduler, JobStatus, parseCronExpression } from './cron.js';