@ryanfw/prompt-orchestration-pipeline 0.14.0 → 0.14.2

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.
@@ -11,7 +11,7 @@
11
11
  />
12
12
  <title>Prompt Pipeline Dashboard</title>
13
13
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
14
- <script type="module" crossorigin src="/assets/index-cjHV9mYW.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-B5HMRkR9.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/style-CoM9SoQF.css">
16
16
  </head>
17
17
  <body>
@@ -18,7 +18,6 @@ import {
18
18
  getJobMetadataPath,
19
19
  getJobPipelinePath,
20
20
  } from "../../config/paths.js";
21
- import { readRawBody } from "../utils/http-utils.js";
22
21
 
23
22
  // Get __dirname equivalent in ES modules
24
23
  const __filename = fileURLToPath(import.meta.url);
@@ -436,21 +435,8 @@ export async function handleJobRestart(req, res, jobId, dataDir, sendJson) {
436
435
  }
437
436
 
438
437
  // Parse optional fromTask from request body for targeted restart
439
- let body = {};
440
- try {
441
- const rawBody = await readRawBody(req);
442
- if (rawBody && rawBody.length > 0) {
443
- const bodyString = rawBody.toString("utf8");
444
- body = JSON.parse(bodyString);
445
- }
446
- } catch (error) {
447
- sendJson(res, 400, {
448
- ok: false,
449
- error: "bad_request",
450
- message: "Invalid JSON in request body",
451
- });
452
- return;
453
- }
438
+ // Note: Express's json() middleware already parsed the body into req.body
439
+ const body = req.body || {};
454
440
 
455
441
  const { fromTask, singleTask } = body;
456
442
 
@@ -462,7 +448,7 @@ export async function handleJobRestart(req, res, jobId, dataDir, sendJson) {
462
448
  if (fromTask && singleTask === true) {
463
449
  await resetSingleTask(jobDir, fromTask, { clearTokenUsage: true });
464
450
  } else if (fromTask) {
465
- await resetJobFromTask(jobDir, fromTask, { clearTokenUsage: true });
451
+ await resetSingleTask(jobDir, fromTask, { clearTokenUsage: true });
466
452
  } else {
467
453
  await resetJobToCleanSlate(jobDir, { clearTokenUsage: true });
468
454
  }
@@ -843,16 +829,80 @@ export async function handleTaskStart(
843
829
  return;
844
830
  }
845
831
 
846
- // Validate task existence
847
- if (!snapshot.tasks || !snapshot.tasks[taskId]) {
848
- sendJson(res, 400, {
832
+ // Read job pipeline config (needed for both task validation and dependency checking)
833
+ const jobPipelinePath = getJobPipelinePath(dataDir, jobId, "current");
834
+ let jobPipeline;
835
+ try {
836
+ const content = await fs.promises.readFile(jobPipelinePath, "utf8");
837
+ jobPipeline = JSON.parse(content);
838
+ } catch (error) {
839
+ sendJson(res, 500, {
849
840
  ok: false,
850
- code: "task_not_found",
851
- message: "Task not found in job",
841
+ code: "pipeline_config_not_found",
842
+ message: "Pipeline configuration not found",
852
843
  });
853
844
  return;
854
845
  }
855
846
 
847
+ // Helper function to extract task name from string or object
848
+ const getTaskName = (t) => (typeof t === "string" ? t : t.name);
849
+
850
+ // Check if task exists in pipeline.json
851
+ const pipelineTaskNames = (jobPipeline.tasks || []).map(getTaskName);
852
+ const taskExistsInPipeline = pipelineTaskNames.includes(taskId);
853
+
854
+ // Validate task existence - check both tasks-status.json and pipeline.json
855
+ if (!snapshot.tasks || !snapshot.tasks[taskId]) {
856
+ if (!taskExistsInPipeline) {
857
+ // Task not found in either file - true error case
858
+ sendJson(res, 400, {
859
+ ok: false,
860
+ code: "task_not_found",
861
+ message: "Task not found in pipeline definition",
862
+ });
863
+ return;
864
+ }
865
+
866
+ // Task exists in pipeline.json but not in tasks-status.json
867
+ // Auto-add it with default pending state
868
+ console.log(
869
+ `[handleTaskStart] Auto-adding missing task "${taskId}" to tasks-status.json for job ${jobId}`
870
+ );
871
+
872
+ await writeJobStatus(jobDir, (s) => {
873
+ if (!s.tasks) {
874
+ s.tasks = {};
875
+ }
876
+ if (!s.tasks[taskId]) {
877
+ s.tasks[taskId] = {
878
+ state: "pending",
879
+ currentStage: null,
880
+ attempts: 0,
881
+ refinementAttempts: 0,
882
+ files: {
883
+ artifacts: [],
884
+ logs: [],
885
+ tmp: [],
886
+ },
887
+ };
888
+ }
889
+ return s;
890
+ });
891
+
892
+ // Re-read the updated snapshot
893
+ try {
894
+ const content = await fs.promises.readFile(statusPath, "utf8");
895
+ snapshot = JSON.parse(content);
896
+ } catch (error) {
897
+ sendJson(res, 500, {
898
+ ok: false,
899
+ code: "internal_error",
900
+ message: "Failed to read updated job status",
901
+ });
902
+ return;
903
+ }
904
+ }
905
+
856
906
  // Validate task state is Pending
857
907
  if (snapshot.tasks[taskId].state !== "pending") {
858
908
  sendJson(res, 400, {
@@ -863,21 +913,6 @@ export async function handleTaskStart(
863
913
  return;
864
914
  }
865
915
 
866
- // Read job pipeline config
867
- const jobPipelinePath = getJobPipelinePath(dataDir, jobId, "current");
868
- let jobPipeline;
869
- try {
870
- const content = await fs.promises.readFile(jobPipelinePath, "utf8");
871
- jobPipeline = JSON.parse(content);
872
- } catch (error) {
873
- sendJson(res, 500, {
874
- ok: false,
875
- code: "pipeline_config_not_found",
876
- message: "Pipeline configuration not found",
877
- });
878
- return;
879
- }
880
-
881
916
  // Validate dependencies via validateUpstreamDone
882
917
  const depCheck = validateUpstreamDone({
883
918
  jobPipelineTasks: jobPipeline.tasks,