ofiere-openclaw-plugin 4.4.1 → 4.7.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 +94 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.4.1",
3
+ "version": "4.7.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
  }
@@ -851,7 +886,10 @@ function registerProjectOps(
851
886
  return ok({ folders: data || [], count: (data || []).length });
852
887
  }
853
888
  case "create_folder": {
854
- if (!params.name || !params.space_id) return err("Missing required: name, space_id");
889
+ const missingFolder: string[] = [];
890
+ if (!params.name) missingFolder.push("name");
891
+ if (!params.space_id) missingFolder.push("space_id");
892
+ if (missingFolder.length > 0) return err(`Missing required: ${missingFolder.join(", ")}`);
855
893
  const { data, error } = await supabase.from("pm_folders").insert({
856
894
  user_id: userId,
857
895
  space_id: params.space_id,
@@ -956,7 +994,7 @@ function registerScheduleOps(
956
994
  recurrence_type: { type: "string", enum: ["none", "hourly", "daily", "weekly", "monthly"] },
957
995
  recurrence_interval: { type: "number", description: "Repeat every N periods" },
958
996
  color: { type: "string", description: "Hex color" },
959
- priority: { type: "number", description: "0-3" },
997
+ priority: { type: ["number", "string"], description: "0-3 (numeric) or 'low'/'medium'/'high'/'critical' (string)" },
960
998
  status: { type: "string", enum: ["scheduled", "completed", "cancelled"] },
961
999
  },
962
1000
  },
@@ -979,6 +1017,21 @@ function registerScheduleOps(
979
1017
  const priorityMap: Record<string, number> = { low: 0, medium: 1, high: 2, critical: 3 };
980
1018
  const pVal = typeof params.priority === "number" ? params.priority
981
1019
  : priorityMap[String(params.priority || "").toLowerCase()] ?? 0;
1020
+
1021
+ // Compute next_run_at from scheduled_date + scheduled_time
1022
+ let nextRunAt: number | null = null;
1023
+ try {
1024
+ const dateStr = params.scheduled_date as string;
1025
+ const timeStr = (params.scheduled_time as string) || "09:00";
1026
+ const dt = new Date(`${dateStr}T${timeStr}:00Z`);
1027
+ if (!isNaN(dt.getTime())) {
1028
+ nextRunAt = Math.floor(dt.getTime() / 1000);
1029
+ // If in the past, bump to now + 60s
1030
+ const nowEpoch = Math.floor(Date.now() / 1000);
1031
+ if (nextRunAt <= nowEpoch) nextRunAt = nowEpoch + 60;
1032
+ }
1033
+ } catch { /* non-fatal */ }
1034
+
982
1035
  const insertData: Record<string, any> = {
983
1036
  id: evtId,
984
1037
  user_id: userId,
@@ -992,6 +1045,7 @@ function registerScheduleOps(
992
1045
  recurrence_type: (params.recurrence_type as string) || "none",
993
1046
  recurrence_interval: (params.recurrence_interval as number) || 1,
994
1047
  status: "scheduled",
1048
+ next_run_at: nextRunAt,
995
1049
  run_count: 0,
996
1050
  color: (params.color as string) || null,
997
1051
  priority: pVal,
@@ -1007,6 +1061,36 @@ function registerScheduleOps(
1007
1061
  "recurrence_type", "recurrence_interval", "status", "color", "priority", "agent_id"]) {
1008
1062
  if ((params as any)[f] !== undefined) upd[f] = (params as any)[f];
1009
1063
  }
1064
+ // Map string priority to number if provided
1065
+ if (upd.priority !== undefined && typeof upd.priority === "string") {
1066
+ const pMap: Record<string, number> = { low: 0, medium: 1, high: 2, critical: 3 };
1067
+ upd.priority = pMap[upd.priority.toLowerCase()] ?? 0;
1068
+ }
1069
+ // Recompute next_run_at when date/time/recurrence changes
1070
+ const dateChanged = params.scheduled_date !== undefined || params.scheduled_time !== undefined;
1071
+ const recurrenceChanged = params.recurrence_type !== undefined;
1072
+ if (dateChanged || recurrenceChanged) {
1073
+ try {
1074
+ // Fetch current event to merge with updates
1075
+ const { data: current } = await supabase.from("scheduler_events").select("scheduled_date, scheduled_time, status").eq("id", params.id).single();
1076
+ const effDate = (upd.scheduled_date || current?.scheduled_date) as string;
1077
+ const effTime = (upd.scheduled_time || current?.scheduled_time || "09:00") as string;
1078
+ const effStatus = (upd.status || current?.status) as string;
1079
+ if (effDate && effStatus !== "completed" && effStatus !== "cancelled") {
1080
+ const dt = new Date(`${effDate}T${effTime}:00Z`);
1081
+ if (!isNaN(dt.getTime())) {
1082
+ let nra = Math.floor(dt.getTime() / 1000);
1083
+ const nowEpoch = Math.floor(Date.now() / 1000);
1084
+ if (nra <= nowEpoch) nra = nowEpoch + 60;
1085
+ upd.next_run_at = nra;
1086
+ }
1087
+ }
1088
+ } catch { /* non-fatal */ }
1089
+ }
1090
+ // Clear next_run_at on completion/cancellation
1091
+ if (upd.status === "completed" || upd.status === "cancelled") {
1092
+ upd.next_run_at = null;
1093
+ }
1010
1094
  const { error } = await supabase.from("scheduler_events").update(upd).eq("id", params.id);
1011
1095
  if (error) return err(error.message);
1012
1096
  return ok({ message: "Event updated", ok: true });