ofiere-openclaw-plugin 3.2.0 → 3.4.0
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/package.json +1 -1
- package/src/tools.ts +63 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM — 9 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, and prompts",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
package/src/tools.ts
CHANGED
|
@@ -191,12 +191,13 @@ function registerTaskOps(
|
|
|
191
191
|
`Manage tasks in the Ofiere PM dashboard. All task operations go through this tool.\n\n` +
|
|
192
192
|
`Actions:\n` +
|
|
193
193
|
`- "list": List/filter tasks. Optional: status, agent_id, space_id, folder_id, limit\n` +
|
|
194
|
-
`- "create": Create a task. Required: title. Optional: agent_id, description, status, priority, space_id, folder_id, start_date, due_date, tags, instructions, execution_plan, goals, constraints, system_prompt\n` +
|
|
194
|
+
`- "create": Create a task. Required: title. Optional: agent_id, description, status, priority, space_id, folder_id, start_date, due_date, tags, instructions, execution_plan, goals, constraints, system_prompt, recurrence_type, recurrence_interval, scheduled_time\n` +
|
|
195
195
|
`- "update": Update a task. Required: task_id. Optional: all create fields + progress\n` +
|
|
196
196
|
`- "delete": Delete task + subtasks. Required: task_id\n\n` +
|
|
197
197
|
`For complex tasks, fill in execution_plan (step-by-step plan), goals, constraints, and system_prompt to help the executing agent.\n` +
|
|
198
198
|
`For simple tasks, just provide title and optionally description.\n` +
|
|
199
199
|
`agent_id: Pass your name to self-assign, another agent's name, or 'none'.\n` +
|
|
200
|
+
`For recurring tasks: set start_date + recurrence_type + recurrence_interval. Example: every 2 minutes = recurrence_type: "minutely", recurrence_interval: 2.\n` +
|
|
200
201
|
`Status: PENDING, IN_PROGRESS, DONE, FAILED | Priority: 0=LOW, 1=MEDIUM, 2=HIGH, 3=CRITICAL`,
|
|
201
202
|
parameters: {
|
|
202
203
|
type: "object",
|
|
@@ -224,8 +225,15 @@ function registerTaskOps(
|
|
|
224
225
|
progress: { type: "number", description: "Progress percentage 0-100 (update only)" },
|
|
225
226
|
space_id: { type: "string", description: "PM Space ID" },
|
|
226
227
|
folder_id: { type: "string", description: "PM Folder ID" },
|
|
227
|
-
start_date: { type: "string", description: "Start date (ISO 8601)" },
|
|
228
|
+
start_date: { type: "string", description: "Start date (ISO 8601). Required for scheduled/recurring tasks." },
|
|
228
229
|
due_date: { type: "string", description: "Due date (ISO 8601)" },
|
|
230
|
+
scheduled_time: { type: "string", description: "Time to execute in HH:MM format (UTC). If omitted, extracted from start_date or defaults to now+60s." },
|
|
231
|
+
recurrence_type: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "How often the task recurs. 'none' for one-shot.",
|
|
234
|
+
enum: ["none", "minutely", "hourly", "daily", "weekly", "monthly"],
|
|
235
|
+
},
|
|
236
|
+
recurrence_interval: { type: "number", description: "Recurrence interval. e.g. recurrence_type='minutely', recurrence_interval=2 → every 2 minutes. Default: 1" },
|
|
229
237
|
tags: {
|
|
230
238
|
type: "array",
|
|
231
239
|
items: { type: "string" },
|
|
@@ -430,11 +438,54 @@ async function handleCreateTask(
|
|
|
430
438
|
const effectiveAgentId = (insertData.agent_id as string) || assignee;
|
|
431
439
|
if (startDate && effectiveAgentId) {
|
|
432
440
|
try {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
441
|
+
// Parse start_date robustly — it can be:
|
|
442
|
+
// "2026-04-19" (date only)
|
|
443
|
+
// "2026-04-19T18:45:00" (local datetime)
|
|
444
|
+
// "2026-04-19 11:45:00+00" (Supabase timestamptz)
|
|
445
|
+
// "2026-04-19T11:45:00.000Z" (ISO UTC)
|
|
446
|
+
const parsedDate = new Date(startDate);
|
|
447
|
+
const explicitScheduledTime = params.scheduled_time as string | undefined;
|
|
448
|
+
|
|
449
|
+
let nextRunAtEpoch: number;
|
|
450
|
+
let scheduledTimeFinal: string;
|
|
451
|
+
let scheduledDateFinal: string;
|
|
452
|
+
|
|
453
|
+
if (!isNaN(parsedDate.getTime())) {
|
|
454
|
+
// Valid date — check if it includes a meaningful time component
|
|
455
|
+
const hasTimeInfo = /[T ]\d{2}:\d{2}/.test(startDate);
|
|
456
|
+
|
|
457
|
+
if (explicitScheduledTime) {
|
|
458
|
+
// Agent explicitly passed a scheduled_time — use date from start_date + explicit time
|
|
459
|
+
const dateStr = parsedDate.toISOString().split("T")[0]; // YYYY-MM-DD
|
|
460
|
+
const dt = new Date(`${dateStr}T${explicitScheduledTime}:00Z`);
|
|
461
|
+
nextRunAtEpoch = Math.floor(dt.getTime() / 1000);
|
|
462
|
+
scheduledTimeFinal = explicitScheduledTime;
|
|
463
|
+
scheduledDateFinal = dateStr;
|
|
464
|
+
} else if (hasTimeInfo) {
|
|
465
|
+
// start_date already contains time — use it directly
|
|
466
|
+
nextRunAtEpoch = Math.floor(parsedDate.getTime() / 1000);
|
|
467
|
+
scheduledTimeFinal = `${String(parsedDate.getUTCHours()).padStart(2, "0")}:${String(parsedDate.getUTCMinutes()).padStart(2, "0")}`;
|
|
468
|
+
scheduledDateFinal = parsedDate.toISOString().split("T")[0];
|
|
469
|
+
} else {
|
|
470
|
+
// Date only, no time — default to 09:00 UTC
|
|
471
|
+
const dateStr = parsedDate.toISOString().split("T")[0];
|
|
472
|
+
const dt = new Date(`${dateStr}T09:00:00Z`);
|
|
473
|
+
nextRunAtEpoch = Math.floor(dt.getTime() / 1000);
|
|
474
|
+
scheduledTimeFinal = "09:00";
|
|
475
|
+
scheduledDateFinal = dateStr;
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
// Unparseable date — fallback to now + 60s
|
|
479
|
+
nextRunAtEpoch = Math.floor(Date.now() / 1000) + 60;
|
|
480
|
+
scheduledTimeFinal = "00:00";
|
|
481
|
+
scheduledDateFinal = new Date().toISOString().split("T")[0];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Safety net: if computed time is in the past, schedule for now + 60s
|
|
485
|
+
const nowEpoch = Math.floor(Date.now() / 1000);
|
|
486
|
+
if (nextRunAtEpoch <= nowEpoch) {
|
|
487
|
+
nextRunAtEpoch = nowEpoch + 60;
|
|
488
|
+
}
|
|
438
489
|
|
|
439
490
|
await supabase.from("scheduler_events").insert({
|
|
440
491
|
id: crypto.randomUUID(),
|
|
@@ -443,13 +494,13 @@ async function handleCreateTask(
|
|
|
443
494
|
agent_id: effectiveAgentId,
|
|
444
495
|
title: params.title,
|
|
445
496
|
description: (params.description as string) || (params.instructions as string) || null,
|
|
446
|
-
scheduled_date:
|
|
447
|
-
scheduled_time:
|
|
497
|
+
scheduled_date: scheduledDateFinal,
|
|
498
|
+
scheduled_time: scheduledTimeFinal,
|
|
448
499
|
duration_minutes: 30,
|
|
449
|
-
recurrence_type: "none",
|
|
450
|
-
recurrence_interval: 1,
|
|
500
|
+
recurrence_type: (params.recurrence_type as string) || "none",
|
|
501
|
+
recurrence_interval: (params.recurrence_interval as number) || 1,
|
|
451
502
|
status: "scheduled",
|
|
452
|
-
next_run_at:
|
|
503
|
+
next_run_at: nextRunAtEpoch,
|
|
453
504
|
run_count: 0,
|
|
454
505
|
priority: params.priority !== undefined ? params.priority : 1,
|
|
455
506
|
});
|