ofiere-openclaw-plugin 4.4.1 → 4.6.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.ts +90 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.4.1",
3
+ "version": "4.6.0",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin for Ofiere PM - 10 meta-tools with 13-action workflow mastery covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, and constellation agent architecture",
6
6
  "keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
package/src/tools.ts CHANGED
@@ -311,7 +311,7 @@ async function handleListTasks(
311
311
  .from("tasks")
312
312
  .select(
313
313
  "id, title, description, status, priority, agent_id, space_id, folder_id, " +
314
- "start_date, due_date, progress, tags, custom_fields, created_at, updated_at",
314
+ "start_date, due_date, progress, tags, custom_fields, completed_at, created_at, updated_at",
315
315
  )
316
316
  .eq("user_id", userId)
317
317
  .order("updated_at", { ascending: false });
@@ -325,16 +325,27 @@ async function handleListTasks(
325
325
  const { data, error } = await query;
326
326
  if (error) return err(error.message);
327
327
 
328
- // Unpack custom_fields for readability
328
+ // BUG 1 fix: normalize legacy statuses to documented enum
329
+ const STATUS_NORMALIZE: Record<string, string> = {
330
+ NEW: "PENDING",
331
+ COMPLETED: "DONE",
332
+ CANCELLED: "FAILED",
333
+ };
334
+
335
+ // Unpack custom_fields for readability, then strip raw custom_fields (BUG 9)
329
336
  const tasks = (data || []).map((t: any) => {
330
337
  const cf = t.custom_fields || {};
338
+ const normalizedStatus = STATUS_NORMALIZE[t.status] || t.status;
339
+ const { custom_fields: _cf, ...rest } = t;
331
340
  return {
332
- ...t,
341
+ ...rest,
342
+ status: normalizedStatus,
333
343
  execution_plan: cf.execution_plan || undefined,
334
344
  goals: cf.goals || undefined,
335
345
  constraints: cf.constraints || undefined,
336
346
  system_prompt: cf.system_prompt || undefined,
337
- instructions: cf.instructions || t.description || undefined,
347
+ instructions: cf.instructions || undefined,
348
+ completed_at: t.completed_at || undefined,
338
349
  };
339
350
  });
340
351
 
@@ -515,14 +526,24 @@ async function handleCreateTask(
515
526
  if (cf.goals) extras.push(`${(cf.goals as any[]).length} goals`);
516
527
  if (cf.constraints) extras.push(`${(cf.constraints as any[]).length} constraints`);
517
528
  if (cf.system_prompt) extras.push("custom system prompt");
518
- if (startDate) extras.push(`scheduled for ${startDate}`);
529
+
530
+ // BUG 6 fix: surface recurrence fields in create response
531
+ const recurrenceInfo = (params.recurrence_type && params.recurrence_type !== "none")
532
+ ? { recurrence_type: params.recurrence_type, recurrence_interval: (params.recurrence_interval as number) || 1 }
533
+ : undefined;
534
+
535
+ // BUG 7 fix: only claim scheduling when bridge actually fired
536
+ const didSchedule = !!(startDate && effectiveAgentId);
537
+ if (didSchedule) extras.push(`scheduled for ${startDate}`);
538
+
519
539
  const extrasStr = extras.length > 0 ? ` with ${extras.join(", ")}` : "";
520
540
 
521
541
  return ok({
522
542
  id,
523
543
  message: `Task "${params.title}" created and assigned to ${assignee || "no one"}${extrasStr}`,
524
544
  task: insertData,
525
- scheduledExecution: startDate ? `Will auto-execute on ${startDate}` : undefined,
545
+ scheduledExecution: didSchedule ? `Will auto-execute on ${startDate}` : undefined,
546
+ recurrence: recurrenceInfo,
526
547
  });
527
548
  } catch (e) {
528
549
  return err(e instanceof Error ? e.message : String(e));
@@ -614,16 +635,30 @@ async function handleUpdateTask(
614
635
  updates.custom_fields = mergedCf;
615
636
  }
616
637
 
638
+ // BUG 2+4 fix: return ALL mutable fields including custom_fields and completed_at
617
639
  const { data, error } = await supabase
618
640
  .from("tasks")
619
641
  .update(updates)
620
642
  .eq("id", params.task_id as string)
621
643
  .eq("user_id", userId)
622
- .select("id, title, status, priority, agent_id, start_date, due_date, progress, updated_at")
644
+ .select("id, title, description, status, priority, agent_id, space_id, folder_id, start_date, due_date, progress, tags, custom_fields, completed_at, updated_at")
623
645
  .single();
624
646
 
625
647
  if (error) return err(error.message);
626
- return ok({ message: `Task "${data?.title}" updated`, task: data });
648
+
649
+ // Unpack custom_fields for readability in response, strip raw (BUG 9 consistency)
650
+ const cf = (data as any)?.custom_fields || {};
651
+ const { custom_fields: _cf, ...rest } = data as any;
652
+ const taskResponse = {
653
+ ...rest,
654
+ execution_plan: cf.execution_plan || undefined,
655
+ goals: cf.goals || undefined,
656
+ constraints: cf.constraints || undefined,
657
+ system_prompt: cf.system_prompt || undefined,
658
+ instructions: cf.instructions || undefined,
659
+ };
660
+
661
+ return ok({ message: `Task "${data?.title}" updated`, task: taskResponse });
627
662
  } catch (e) {
628
663
  return err(e instanceof Error ? e.message : String(e));
629
664
  }
@@ -956,7 +991,7 @@ function registerScheduleOps(
956
991
  recurrence_type: { type: "string", enum: ["none", "hourly", "daily", "weekly", "monthly"] },
957
992
  recurrence_interval: { type: "number", description: "Repeat every N periods" },
958
993
  color: { type: "string", description: "Hex color" },
959
- priority: { type: "number", description: "0-3" },
994
+ priority: { type: ["number", "string"], description: "0-3 (numeric) or 'low'/'medium'/'high'/'critical' (string)" },
960
995
  status: { type: "string", enum: ["scheduled", "completed", "cancelled"] },
961
996
  },
962
997
  },
@@ -979,6 +1014,21 @@ function registerScheduleOps(
979
1014
  const priorityMap: Record<string, number> = { low: 0, medium: 1, high: 2, critical: 3 };
980
1015
  const pVal = typeof params.priority === "number" ? params.priority
981
1016
  : priorityMap[String(params.priority || "").toLowerCase()] ?? 0;
1017
+
1018
+ // Compute next_run_at from scheduled_date + scheduled_time
1019
+ let nextRunAt: number | null = null;
1020
+ try {
1021
+ const dateStr = params.scheduled_date as string;
1022
+ const timeStr = (params.scheduled_time as string) || "09:00";
1023
+ const dt = new Date(`${dateStr}T${timeStr}:00Z`);
1024
+ if (!isNaN(dt.getTime())) {
1025
+ nextRunAt = Math.floor(dt.getTime() / 1000);
1026
+ // If in the past, bump to now + 60s
1027
+ const nowEpoch = Math.floor(Date.now() / 1000);
1028
+ if (nextRunAt <= nowEpoch) nextRunAt = nowEpoch + 60;
1029
+ }
1030
+ } catch { /* non-fatal */ }
1031
+
982
1032
  const insertData: Record<string, any> = {
983
1033
  id: evtId,
984
1034
  user_id: userId,
@@ -992,6 +1042,7 @@ function registerScheduleOps(
992
1042
  recurrence_type: (params.recurrence_type as string) || "none",
993
1043
  recurrence_interval: (params.recurrence_interval as number) || 1,
994
1044
  status: "scheduled",
1045
+ next_run_at: nextRunAt,
995
1046
  run_count: 0,
996
1047
  color: (params.color as string) || null,
997
1048
  priority: pVal,
@@ -1007,6 +1058,36 @@ function registerScheduleOps(
1007
1058
  "recurrence_type", "recurrence_interval", "status", "color", "priority", "agent_id"]) {
1008
1059
  if ((params as any)[f] !== undefined) upd[f] = (params as any)[f];
1009
1060
  }
1061
+ // Map string priority to number if provided
1062
+ if (upd.priority !== undefined && typeof upd.priority === "string") {
1063
+ const pMap: Record<string, number> = { low: 0, medium: 1, high: 2, critical: 3 };
1064
+ upd.priority = pMap[upd.priority.toLowerCase()] ?? 0;
1065
+ }
1066
+ // Recompute next_run_at when date/time/recurrence changes
1067
+ const dateChanged = params.scheduled_date !== undefined || params.scheduled_time !== undefined;
1068
+ const recurrenceChanged = params.recurrence_type !== undefined;
1069
+ if (dateChanged || recurrenceChanged) {
1070
+ try {
1071
+ // Fetch current event to merge with updates
1072
+ const { data: current } = await supabase.from("scheduler_events").select("scheduled_date, scheduled_time, status").eq("id", params.id).single();
1073
+ const effDate = (upd.scheduled_date || current?.scheduled_date) as string;
1074
+ const effTime = (upd.scheduled_time || current?.scheduled_time || "09:00") as string;
1075
+ const effStatus = (upd.status || current?.status) as string;
1076
+ if (effDate && effStatus !== "completed" && effStatus !== "cancelled") {
1077
+ const dt = new Date(`${effDate}T${effTime}:00Z`);
1078
+ if (!isNaN(dt.getTime())) {
1079
+ let nra = Math.floor(dt.getTime() / 1000);
1080
+ const nowEpoch = Math.floor(Date.now() / 1000);
1081
+ if (nra <= nowEpoch) nra = nowEpoch + 60;
1082
+ upd.next_run_at = nra;
1083
+ }
1084
+ }
1085
+ } catch { /* non-fatal */ }
1086
+ }
1087
+ // Clear next_run_at on completion/cancellation
1088
+ if (upd.status === "completed" || upd.status === "cancelled") {
1089
+ upd.next_run_at = null;
1090
+ }
1010
1091
  const { error } = await supabase.from("scheduler_events").update(upd).eq("id", params.id);
1011
1092
  if (error) return err(error.message);
1012
1093
  return ok({ message: "Event updated", ok: true });