powr-sdk-api 4.3.10 → 4.3.12

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.
@@ -475,11 +475,35 @@ class TasksManager {
475
475
 
476
476
  // Log workflow execution
477
477
  await this.logWorkflowExecution(workflow, results);
478
+
479
+ // For one-time workflows, deactivate after execution
480
+ if (workflow.schedule && !this.isCronExpression(workflow.schedule)) {
481
+ try {
482
+ const db = await getDb();
483
+ await db.collection("tasks").updateOne({
484
+ _id: workflow._id
485
+ }, {
486
+ $set: {
487
+ isActive: false
488
+ }
489
+ });
490
+ console.log(`🔄 Deactivated one-time workflow: ${workflow.name}`);
491
+ } catch (error) {
492
+ console.error("❌ Failed to deactivate workflow:", error);
493
+ }
494
+ }
495
+
496
+ // Determine overall success - consider it successful if at least one action executed successfully
497
+ const executedActions = results.filter(r => r.type === 'action' && !r.skipped);
498
+ const successfulActions = executedActions.filter(r => r.success);
499
+ const overallSuccess = executedActions.length === 0 || successfulActions.length > 0;
478
500
  return {
479
- success: workflowSuccess,
501
+ success: overallSuccess,
480
502
  workflowName: workflow.name,
481
503
  stepsExecuted: results.length,
482
- results: results
504
+ results: results,
505
+ actionsExecuted: executedActions.length,
506
+ actionsSuccessful: successfulActions.length
483
507
  };
484
508
  } catch (error) {
485
509
  console.error(`❌ Workflow execution failed: ${workflow.name}`, error);
@@ -554,15 +578,35 @@ class TasksManager {
554
578
  return true; // No condition, always execute
555
579
  }
556
580
 
557
- // Simple condition evaluation
558
- // For now, support basic conditions like "step1 === true"
581
+ // Parse condition like "step1.result === 'rain'" or "step1.result !== 'rain'"
559
582
  const condition = step.condition;
583
+
584
+ // Extract step ID and condition
585
+ const stepMatch = condition.match(/step(\d+)\.result\s*(===|!==)\s*['"]([^'"]+)['"]/);
586
+ if (stepMatch) {
587
+ const stepId = stepMatch[1];
588
+ const operator = stepMatch[2];
589
+ const expectedValue = stepMatch[3];
590
+ const stepResult = previousResults.find(r => r.stepId === stepId);
591
+ if (stepResult && stepResult.result) {
592
+ var _stepResult$result$da;
593
+ // Get actual value from the result structure
594
+ const actualValue = stepResult.result.actualValue || ((_stepResult$result$da = stepResult.result.data) === null || _stepResult$result$da === void 0 ? void 0 : _stepResult$result$da.currentWeather) || stepResult.result.condition || 'unknown';
595
+ console.log(`🔍 Condition check: ${actualValue} ${operator} ${expectedValue}`);
596
+ if (operator === '===') {
597
+ return actualValue.toLowerCase() === expectedValue.toLowerCase();
598
+ } else if (operator === '!==') {
599
+ return actualValue.toLowerCase() !== expectedValue.toLowerCase();
600
+ }
601
+ }
602
+ }
603
+
604
+ // Fallback: check for simple boolean conditions
560
605
  if (condition.includes('step1') && condition.includes('=== true')) {
561
606
  const step1Result = previousResults.find(r => r.stepId === 'step1');
562
607
  return step1Result && step1Result.conditionMet === true;
563
608
  }
564
-
565
- // Add more condition parsing as needed
609
+ console.log(`⚠️ Could not parse condition: ${condition}, defaulting to execute`);
566
610
  return true; // Default to execute if condition parsing fails
567
611
  }
568
612
 
@@ -589,12 +633,20 @@ class TasksManager {
589
633
  async logWorkflowExecution(workflow, results) {
590
634
  try {
591
635
  const db = await getDb();
636
+
637
+ // Calculate overall success
638
+ const executedActions = results.filter(r => r.type === 'action' && !r.skipped);
639
+ const successfulActions = executedActions.filter(r => r.success);
640
+ const overallSuccess = executedActions.length === 0 || successfulActions.length > 0;
592
641
  await db.collection("workflow_executions").insertOne({
593
642
  workflowId: workflow._id,
594
643
  workflowName: workflow.name,
595
644
  userId: workflow.userId,
596
645
  projectId: workflow.projectId,
597
646
  results: results,
647
+ success: overallSuccess,
648
+ actionsExecuted: executedActions.length,
649
+ actionsSuccessful: successfulActions.length,
598
650
  executedAt: new Date()
599
651
  });
600
652
  } catch (error) {
@@ -602,6 +654,61 @@ class TasksManager {
602
654
  }
603
655
  }
604
656
 
657
+ // Get execution history for a task
658
+ async getTaskExecutions(taskId, userId, projectId, isAdmin = false) {
659
+ try {
660
+ const db = await getDb();
661
+
662
+ // First verify the task exists and user has access
663
+ let taskQuery = {
664
+ _id: new ObjectId(taskId)
665
+ };
666
+ if (isAdmin) {
667
+ taskQuery.projectId = projectId;
668
+ } else {
669
+ taskQuery.userId = userId;
670
+ taskQuery.projectId = projectId;
671
+ }
672
+ const task = await db.collection("tasks").findOne(taskQuery);
673
+ if (!task) {
674
+ return {
675
+ success: false,
676
+ message: "Task not found"
677
+ };
678
+ }
679
+
680
+ // Get execution history based on task type
681
+ let executions = [];
682
+ if (task.type === 'workflow') {
683
+ // Get workflow executions
684
+ executions = await db.collection("workflow_executions").find({
685
+ workflowId: new ObjectId(taskId)
686
+ }).sort({
687
+ executedAt: -1
688
+ }).limit(50) // Limit to last 50 executions
689
+ .toArray();
690
+ } else {
691
+ // Get task executions
692
+ executions = await db.collection("task_executions").find({
693
+ taskId: new ObjectId(taskId)
694
+ }).sort({
695
+ executedAt: -1
696
+ }).limit(50) // Limit to last 50 executions
697
+ .toArray();
698
+ }
699
+ return {
700
+ success: true,
701
+ executions
702
+ };
703
+ } catch (error) {
704
+ console.error("❌ Failed to get task executions:", error);
705
+ return {
706
+ success: false,
707
+ message: error.message
708
+ };
709
+ }
710
+ }
711
+
605
712
  // Check if a string is a valid timestamp
606
713
  isTimestamp(value) {
607
714
  try {
@@ -612,6 +719,15 @@ class TasksManager {
612
719
  }
613
720
  }
614
721
 
722
+ // Check if a string is a cron expression
723
+ isCronExpression(value) {
724
+ if (!value || typeof value !== 'string') return false;
725
+
726
+ // Basic cron pattern: 5 or 6 fields separated by spaces
727
+ const cronPattern = /^(\*|\d+|\d+-\d+|\d+\/\d+)(\s+(\*|\d+|\d+-\d+|\d+\/\d+)){4,5}$/;
728
+ return cronPattern.test(value.trim());
729
+ }
730
+
615
731
  // Check if a string is a cron expression
616
732
  isCronExpression(value) {
617
733
  try {
@@ -275,6 +275,32 @@ router.delete("/:taskId", async (req, res) => {
275
275
  }
276
276
  });
277
277
 
278
+ // GET /tasks/:taskId/executions - Get execution history for a task
279
+ router.get("/:taskId/executions", async (req, res) => {
280
+ try {
281
+ const taskId = req.params.taskId;
282
+ const isAdmin = req.user.access === 100;
283
+ const result = await tasksManager.getTaskExecutions(taskId, req.user.powrId, req.projectId, isAdmin);
284
+ if (result.success) {
285
+ res.json({
286
+ success: true,
287
+ executions: result.executions
288
+ });
289
+ } else {
290
+ res.status(400).json({
291
+ success: false,
292
+ message: result.message
293
+ });
294
+ }
295
+ } catch (error) {
296
+ console.error("❌ Error retrieving task executions:", error);
297
+ res.status(500).json({
298
+ success: false,
299
+ message: "Failed to retrieve task executions"
300
+ });
301
+ }
302
+ });
303
+
278
304
  // POST /tasks/execute-scheduled - Execute all scheduled tasks (called by Atlas)
279
305
  router.post("/execute-scheduled", async (req, res) => {
280
306
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "4.3.10",
3
+ "version": "4.3.12",
4
4
  "description": "Shared API core library for PowrStack projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",