ofiere-openclaw-plugin 4.1.0 → 4.3.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 +52 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.1.0",
3
+ "version": "4.3.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
@@ -1334,17 +1334,24 @@ function registerWorkflowOps(
1334
1334
  return { wf: data, error: null };
1335
1335
  }
1336
1336
 
1337
- // Helper: save updated graph back to DB
1337
+ // Helper: save updated graph back to DB with verification re-read
1338
1338
  async function saveGraph(wfId: string, nodes: any[], edges: any[]) {
1339
- const { data, error } = await supabase
1339
+ const { error } = await supabase
1340
1340
  .from("workflows")
1341
1341
  .update({ nodes, edges, updated_at: new Date().toISOString() })
1342
1342
  .eq("id", wfId)
1343
+ .eq("user_id", userId);
1344
+ if (error) return { wf: null, error: error.message };
1345
+ // Verification re-read — guarantees we return the actual persisted state,
1346
+ // not a potentially stale response from the update's connection.
1347
+ const { data: verified, error: readErr } = await supabase
1348
+ .from("workflows")
1349
+ .select("*")
1350
+ .eq("id", wfId)
1343
1351
  .eq("user_id", userId)
1344
- .select()
1345
1352
  .single();
1346
- if (error) return { wf: null, error: error.message };
1347
- return { wf: data, error: null };
1353
+ if (readErr) return { wf: null, error: readErr.message };
1354
+ return { wf: verified, error: null };
1348
1355
  }
1349
1356
 
1350
1357
  switch (action) {
@@ -1358,9 +1365,12 @@ function registerWorkflowOps(
1358
1365
  case "get": {
1359
1366
  const wfId = (params.id || params.workflow_id) as string;
1360
1367
  if (!wfId) return err("Missing required: id");
1361
- const { data, error } = await supabase.from("workflows").select("*").eq("id", wfId).eq("user_id", userId).single();
1362
- if (error) return err(error.message);
1363
- return ok({ workflow: data });
1368
+ // Route through serialization queue so reads wait for pending mutations
1369
+ return sequentialWorkflowOp(wfId, async () => {
1370
+ const { data, error } = await supabase.from("workflows").select("*").eq("id", wfId).eq("user_id", userId).single();
1371
+ if (error) return err(error.message);
1372
+ return ok({ workflow: data });
1373
+ });
1364
1374
  }
1365
1375
  case "create": {
1366
1376
  if (!params.name) return err("Missing required: name");
@@ -1389,8 +1399,37 @@ function registerWorkflowOps(
1389
1399
  finalNodes.unshift(triggerNode);
1390
1400
  }
1391
1401
 
1392
- // Build edges
1393
- let finalEdges = ((params.edges as any[]) || []).map((e: any, i: number) => normalizeEdge(e, i));
1402
+ // Build edges — remap IDs from pre-normalization to post-normalization
1403
+ const idRemap = new Map<string, string>();
1404
+ rawNodes.forEach((raw, i) => {
1405
+ if (raw.id && finalNodes[hasTrigger ? i : i + 1]?.id !== raw.id) {
1406
+ idRemap.set(raw.id, finalNodes[hasTrigger ? i : i + 1].id);
1407
+ }
1408
+ });
1409
+
1410
+ let finalEdges: any[];
1411
+ const suppliedEdges = (params.edges as any[]) || [];
1412
+
1413
+ if (suppliedEdges.length > 0) {
1414
+ // Remap source/target IDs in user-supplied edges
1415
+ finalEdges = suppliedEdges.map((e: any, i: number) => {
1416
+ const remapped = {
1417
+ ...e,
1418
+ source: idRemap.get(e.source) || e.source,
1419
+ target: idRemap.get(e.target) || e.target,
1420
+ };
1421
+ return normalizeEdge(remapped, i);
1422
+ });
1423
+ } else {
1424
+ // No edges supplied — auto-chain all nodes in order
1425
+ finalEdges = [];
1426
+ for (let i = 0; i < finalNodes.length - 1; i++) {
1427
+ finalEdges.push(normalizeEdge({
1428
+ source: finalNodes[i].id,
1429
+ target: finalNodes[i + 1].id,
1430
+ }, i));
1431
+ }
1432
+ }
1394
1433
 
1395
1434
  // Auto-wire trigger to first non-trigger node if no edge connects from trigger
1396
1435
  if (!hasTrigger && finalNodes.length > 1) {
@@ -1398,11 +1437,10 @@ function registerWorkflowOps(
1398
1437
  const firstStepId = finalNodes[1].id;
1399
1438
  const triggerHasEdge = finalEdges.some((e: any) => e.source === triggerId);
1400
1439
  if (!triggerHasEdge) {
1401
- finalEdges.unshift({
1402
- id: `edge-trigger-${Date.now()}`,
1440
+ finalEdges.unshift(normalizeEdge({
1403
1441
  source: triggerId,
1404
1442
  target: firstStepId,
1405
- });
1443
+ }, finalEdges.length));
1406
1444
  }
1407
1445
  }
1408
1446
 
@@ -1453,7 +1491,7 @@ function registerWorkflowOps(
1453
1491
  if (!wfId) return err("Missing required: workflow_id");
1454
1492
  const { data, error } = await supabase.from("workflow_runs").select("*")
1455
1493
  .eq("workflow_id", wfId)
1456
- .order("created_at", { ascending: false })
1494
+ .order("started_at", { ascending: false })
1457
1495
  .limit((params.limit as number) || 20);
1458
1496
  if (error) return err(error.message);
1459
1497
  return ok({ runs: data || [], count: (data || []).length });