@ryanfw/prompt-orchestration-pipeline 0.9.1 → 0.11.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.
- package/package.json +2 -1
- package/src/api/index.js +38 -1
- package/src/components/DAGGrid.jsx +24 -7
- package/src/components/JobDetail.jsx +11 -0
- package/src/components/TaskDetailSidebar.jsx +27 -3
- package/src/components/UploadSeed.jsx +2 -2
- package/src/config/log-events.js +77 -0
- package/src/core/file-io.js +202 -7
- package/src/core/orchestrator.js +140 -4
- package/src/core/pipeline-runner.js +84 -6
- package/src/core/status-initializer.js +155 -0
- package/src/core/status-writer.js +151 -13
- package/src/core/symlink-utils.js +196 -0
- package/src/core/task-runner.js +37 -7
- package/src/ui/client/adapters/job-adapter.js +21 -2
- package/src/ui/client/hooks/useJobDetailWithUpdates.js +92 -0
- package/src/ui/dist/assets/{index-DqkbzXZ1.js → index-DeDzq-Kk.js} +129 -14
- package/src/ui/dist/assets/style-aBtD_Yrs.css +62 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/server.js +201 -109
- package/src/ui/zip-utils.js +103 -0
- package/src/ui/dist/assets/style-DBF9NQGk.css +0 -62
|
@@ -20023,7 +20023,9 @@ function normalizeTasks(rawTasks) {
|
|
|
20023
20023
|
},
|
|
20024
20024
|
artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0,
|
|
20025
20025
|
// Preserve tokenUsage if present
|
|
20026
|
-
...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {}
|
|
20026
|
+
...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {},
|
|
20027
|
+
// Preserve error object if present
|
|
20028
|
+
...t2 && t2.error ? { error: t2.error } : {}
|
|
20027
20029
|
};
|
|
20028
20030
|
tasks[name] = taskObj;
|
|
20029
20031
|
});
|
|
@@ -20045,9 +20047,21 @@ function normalizeTasks(rawTasks) {
|
|
|
20045
20047
|
// Preserve stage metadata for DAG visualization
|
|
20046
20048
|
...typeof t2?.currentStage === "string" && t2.currentStage.length > 0 ? { currentStage: t2.currentStage } : {},
|
|
20047
20049
|
...typeof t2?.failedStage === "string" && t2.failedStage.length > 0 ? { failedStage: t2.failedStage } : {},
|
|
20050
|
+
// Prefer new files.* schema, fallback to legacy artifacts
|
|
20051
|
+
files: t2 && t2.files ? {
|
|
20052
|
+
artifacts: Array.isArray(t2.files.artifacts) ? t2.files.artifacts.slice() : [],
|
|
20053
|
+
logs: Array.isArray(t2.files.logs) ? t2.files.logs.slice() : [],
|
|
20054
|
+
tmp: Array.isArray(t2.files.tmp) ? t2.files.tmp.slice() : []
|
|
20055
|
+
} : {
|
|
20056
|
+
artifacts: [],
|
|
20057
|
+
logs: [],
|
|
20058
|
+
tmp: []
|
|
20059
|
+
},
|
|
20048
20060
|
artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0,
|
|
20049
20061
|
// Preserve tokenUsage if present
|
|
20050
|
-
...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {}
|
|
20062
|
+
...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {},
|
|
20063
|
+
// Preserve error object if present
|
|
20064
|
+
...t2 && t2.error ? { error: t2.error } : {}
|
|
20051
20065
|
};
|
|
20052
20066
|
});
|
|
20053
20067
|
return { tasks, warnings };
|
|
@@ -20068,7 +20082,7 @@ function computeJobSummaryStats(tasks) {
|
|
|
20068
20082
|
function adaptJobSummary(apiJob) {
|
|
20069
20083
|
const id = apiJob.jobId;
|
|
20070
20084
|
const name = apiJob.title || "";
|
|
20071
|
-
const rawTasks = apiJob.tasks;
|
|
20085
|
+
const rawTasks = apiJob.tasks || apiJob.tasksStatus;
|
|
20072
20086
|
const location = apiJob.location;
|
|
20073
20087
|
const current = apiJob.current;
|
|
20074
20088
|
const currentStage = apiJob.currentStage;
|
|
@@ -20112,7 +20126,7 @@ function adaptJobSummary(apiJob) {
|
|
|
20112
20126
|
function adaptJobDetail(apiDetail) {
|
|
20113
20127
|
const id = apiDetail.jobId;
|
|
20114
20128
|
const name = apiDetail.title || "";
|
|
20115
|
-
const rawTasks = apiDetail.tasks;
|
|
20129
|
+
const rawTasks = apiDetail.tasks || apiDetail.tasksStatus;
|
|
20116
20130
|
const location = apiDetail.location;
|
|
20117
20131
|
const current = apiDetail.current;
|
|
20118
20132
|
const currentStage = apiDetail.currentStage;
|
|
@@ -20930,7 +20944,7 @@ function UploadSeed({ onUploadSuccess }) {
|
|
|
20930
20944
|
" ",
|
|
20931
20945
|
"or drag and drop"
|
|
20932
20946
|
] }),
|
|
20933
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500", children: "JSON files only" })
|
|
20947
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500", children: "JSON or zip files only" })
|
|
20934
20948
|
] })
|
|
20935
20949
|
}
|
|
20936
20950
|
),
|
|
@@ -20939,7 +20953,7 @@ function UploadSeed({ onUploadSuccess }) {
|
|
|
20939
20953
|
{
|
|
20940
20954
|
ref: fileInputRef,
|
|
20941
20955
|
type: "file",
|
|
20942
|
-
accept: ".json",
|
|
20956
|
+
accept: ".json,.zip",
|
|
20943
20957
|
className: "hidden",
|
|
20944
20958
|
onChange: handleFileChange,
|
|
20945
20959
|
"data-testid": "file-input"
|
|
@@ -21706,6 +21720,7 @@ function TaskDetailSidebar({
|
|
|
21706
21720
|
jobId,
|
|
21707
21721
|
taskId,
|
|
21708
21722
|
taskBody,
|
|
21723
|
+
taskError,
|
|
21709
21724
|
filesByTypeForItem = () => ({ artifacts: [], logs: [], tmp: [] }),
|
|
21710
21725
|
task,
|
|
21711
21726
|
onClose,
|
|
@@ -21715,6 +21730,7 @@ function TaskDetailSidebar({
|
|
|
21715
21730
|
const [filePaneType, setFilePaneType] = reactExports.useState("artifacts");
|
|
21716
21731
|
const [filePaneOpen, setFilePaneOpen] = reactExports.useState(false);
|
|
21717
21732
|
const [filePaneFilename, setFilePaneFilename] = reactExports.useState(null);
|
|
21733
|
+
const [showStack, setShowStack] = reactExports.useState(false);
|
|
21718
21734
|
const closeButtonRef = reactExports.useRef(null);
|
|
21719
21735
|
const getHeaderClasses2 = (status2) => {
|
|
21720
21736
|
switch (status2) {
|
|
@@ -21794,7 +21810,29 @@ function TaskDetailSidebar({
|
|
|
21794
21810
|
}
|
|
21795
21811
|
),
|
|
21796
21812
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 space-y-8 overflow-y-auto h-full", children: [
|
|
21797
|
-
status === TaskState.FAILED && taskBody && /* @__PURE__ */ jsxRuntimeExports.
|
|
21813
|
+
status === TaskState.FAILED && (taskError?.message || taskBody) && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { "aria-label": "Error", children: [
|
|
21814
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(n$2, { role: "alert", "aria-live": "assertive", children: /* @__PURE__ */ jsxRuntimeExports.jsx(u$1, { className: "whitespace-pre-wrap break-words", children: taskError?.message || taskBody }) }),
|
|
21815
|
+
taskError?.stack && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3", children: [
|
|
21816
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
21817
|
+
"button",
|
|
21818
|
+
{
|
|
21819
|
+
onClick: () => setShowStack(!showStack),
|
|
21820
|
+
className: "text-sm text-blue-600 hover:text-blue-800 underline",
|
|
21821
|
+
"aria-expanded": showStack,
|
|
21822
|
+
"aria-controls": "error-stack",
|
|
21823
|
+
children: showStack ? "Hide stack" : "Show stack"
|
|
21824
|
+
}
|
|
21825
|
+
),
|
|
21826
|
+
showStack && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
21827
|
+
"pre",
|
|
21828
|
+
{
|
|
21829
|
+
id: "error-stack",
|
|
21830
|
+
className: "mt-2 p-2 bg-gray-50 border rounded text-xs font-mono max-h-64 overflow-auto whitespace-pre-wrap",
|
|
21831
|
+
children: taskError.stack
|
|
21832
|
+
}
|
|
21833
|
+
)
|
|
21834
|
+
] })
|
|
21835
|
+
] }),
|
|
21798
21836
|
/* @__PURE__ */ jsxRuntimeExports.jsx("section", { className: "mt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
21799
21837
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-semibold text-gray-900", children: "Files" }),
|
|
21800
21838
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded-lg border border-gray-200 bg-gray-50 p-1", children: [
|
|
@@ -22187,6 +22225,9 @@ const getHeaderClasses = (status) => {
|
|
|
22187
22225
|
const canShowRestart = (status) => {
|
|
22188
22226
|
return status === TaskState.FAILED || status === TaskState.DONE;
|
|
22189
22227
|
};
|
|
22228
|
+
const areEqualTaskCardProps = (prevProps, nextProps) => {
|
|
22229
|
+
return prevProps.item === nextProps.item && prevProps.idx === nextProps.idx && prevProps.status === nextProps.status && prevProps.isActive === nextProps.isActive && prevProps.canRestart === nextProps.canRestart && prevProps.isSubmitting === nextProps.isSubmitting && prevProps.disabledReason === nextProps.disabledReason && prevProps.onClick === nextProps.onClick && prevProps.onKeyDown === nextProps.onKeyDown && prevProps.handleRestartClick === nextProps.handleRestartClick;
|
|
22230
|
+
};
|
|
22190
22231
|
const TaskCard = reactExports.memo(function TaskCard2({
|
|
22191
22232
|
item,
|
|
22192
22233
|
idx,
|
|
@@ -22195,7 +22236,7 @@ const TaskCard = reactExports.memo(function TaskCard2({
|
|
|
22195
22236
|
isActive,
|
|
22196
22237
|
canRestart,
|
|
22197
22238
|
isSubmitting,
|
|
22198
|
-
|
|
22239
|
+
disabledReason,
|
|
22199
22240
|
onClick,
|
|
22200
22241
|
onKeyDown,
|
|
22201
22242
|
handleRestartClick
|
|
@@ -22293,7 +22334,7 @@ const TaskCard = reactExports.memo(function TaskCard2({
|
|
|
22293
22334
|
onClick: (e2) => handleRestartClick(e2, item.id),
|
|
22294
22335
|
disabled: !canRestart || isSubmitting,
|
|
22295
22336
|
className: "text-xs cursor-pointer disabled:cursor-not-allowed",
|
|
22296
|
-
title: !canRestart ?
|
|
22337
|
+
title: !canRestart ? disabledReason : `Restart job from ${item.id}`,
|
|
22297
22338
|
children: "Restart"
|
|
22298
22339
|
}
|
|
22299
22340
|
) })
|
|
@@ -22301,14 +22342,15 @@ const TaskCard = reactExports.memo(function TaskCard2({
|
|
|
22301
22342
|
]
|
|
22302
22343
|
}
|
|
22303
22344
|
);
|
|
22304
|
-
});
|
|
22345
|
+
}, areEqualTaskCardProps);
|
|
22305
22346
|
function DAGGrid({
|
|
22306
22347
|
items,
|
|
22307
22348
|
cols = 3,
|
|
22308
22349
|
cardClass = "",
|
|
22309
22350
|
activeIndex = 0,
|
|
22310
22351
|
jobId,
|
|
22311
|
-
filesByTypeForItem = () => createEmptyTaskFiles()
|
|
22352
|
+
filesByTypeForItem = () => createEmptyTaskFiles(),
|
|
22353
|
+
taskById = {}
|
|
22312
22354
|
}) {
|
|
22313
22355
|
const overlayRef = reactExports.useRef(null);
|
|
22314
22356
|
const gridRef = reactExports.useRef(null);
|
|
@@ -22458,7 +22500,7 @@ function DAGGrid({
|
|
|
22458
22500
|
cancelAnimationFrame(rafRef.current);
|
|
22459
22501
|
}
|
|
22460
22502
|
};
|
|
22461
|
-
}, [items, effectiveCols, visualOrder]);
|
|
22503
|
+
}, [items.length, effectiveCols, visualOrder]);
|
|
22462
22504
|
const getStatus = (index2) => {
|
|
22463
22505
|
const item = items[index2];
|
|
22464
22506
|
const s2 = item?.status;
|
|
@@ -22653,6 +22695,7 @@ function DAGGrid({
|
|
|
22653
22695
|
const status = getStatus(idx);
|
|
22654
22696
|
const isActive = idx === activeIndex;
|
|
22655
22697
|
const canRestart = isRestartEnabled();
|
|
22698
|
+
const restartDisabledReason = getRestartDisabledReason();
|
|
22656
22699
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22657
22700
|
TaskCard,
|
|
22658
22701
|
{
|
|
@@ -22662,7 +22705,7 @@ function DAGGrid({
|
|
|
22662
22705
|
isActive,
|
|
22663
22706
|
canRestart,
|
|
22664
22707
|
isSubmitting,
|
|
22665
|
-
|
|
22708
|
+
disabledReason: restartDisabledReason,
|
|
22666
22709
|
onClick: () => {
|
|
22667
22710
|
setOpenIdx(idx);
|
|
22668
22711
|
},
|
|
@@ -22689,6 +22732,7 @@ function DAGGrid({
|
|
|
22689
22732
|
jobId,
|
|
22690
22733
|
taskId: items[openIdx]?.id || `task-${openIdx}`,
|
|
22691
22734
|
taskBody: items[openIdx]?.body || null,
|
|
22735
|
+
taskError: taskById[items[openIdx]?.id]?.error || null,
|
|
22692
22736
|
filesByTypeForItem,
|
|
22693
22737
|
task: items[openIdx],
|
|
22694
22738
|
taskIndex: openIdx,
|
|
@@ -22874,6 +22918,12 @@ function JobDetail({ job, pipeline }) {
|
|
|
22874
22918
|
}
|
|
22875
22919
|
return item;
|
|
22876
22920
|
});
|
|
22921
|
+
const allReused = newItems.every(
|
|
22922
|
+
(item, index2) => item === prevItems[index2]
|
|
22923
|
+
);
|
|
22924
|
+
if (allReused && prevItems.length === newItems.length) {
|
|
22925
|
+
return prevItems;
|
|
22926
|
+
}
|
|
22877
22927
|
prevDagItemsRef.current = newItems;
|
|
22878
22928
|
return newItems;
|
|
22879
22929
|
}, [stableDagItems]);
|
|
@@ -22894,7 +22944,8 @@ function JobDetail({ job, pipeline }) {
|
|
|
22894
22944
|
items: dagItems,
|
|
22895
22945
|
activeIndex,
|
|
22896
22946
|
jobId: job.id,
|
|
22897
|
-
filesByTypeForItem
|
|
22947
|
+
filesByTypeForItem,
|
|
22948
|
+
taskById
|
|
22898
22949
|
}
|
|
22899
22950
|
) });
|
|
22900
22951
|
}
|
|
@@ -23153,6 +23204,7 @@ function useJobDetailWithUpdates(jobId) {
|
|
|
23153
23204
|
newEs.addEventListener("job:removed", onJobRemoved);
|
|
23154
23205
|
newEs.addEventListener("status:changed", onStatusChanged);
|
|
23155
23206
|
newEs.addEventListener("state:change", onStateChange);
|
|
23207
|
+
newEs.addEventListener("task:updated", onTaskUpdated);
|
|
23156
23208
|
newEs.addEventListener("error", onError);
|
|
23157
23209
|
esRef.current = newEs;
|
|
23158
23210
|
} catch (err) {
|
|
@@ -23172,6 +23224,66 @@ function useJobDetailWithUpdates(jobId) {
|
|
|
23172
23224
|
eventQueue.current = (eventQueue.current || []).concat(eventObj);
|
|
23173
23225
|
return;
|
|
23174
23226
|
}
|
|
23227
|
+
if (type === "task:updated") {
|
|
23228
|
+
const p2 = payload || {};
|
|
23229
|
+
const { jobId: eventJobId, taskId, task } = p2;
|
|
23230
|
+
if (eventJobId && eventJobId !== jobId) {
|
|
23231
|
+
return;
|
|
23232
|
+
}
|
|
23233
|
+
if (!taskId || !task) {
|
|
23234
|
+
return;
|
|
23235
|
+
}
|
|
23236
|
+
startTransition(() => {
|
|
23237
|
+
setData((prev) => {
|
|
23238
|
+
if (!prev || !prev.tasks) {
|
|
23239
|
+
return prev;
|
|
23240
|
+
}
|
|
23241
|
+
const prevTask = prev.tasks[taskId];
|
|
23242
|
+
const fieldsToCompare = [
|
|
23243
|
+
"state",
|
|
23244
|
+
"currentStage",
|
|
23245
|
+
"failedStage",
|
|
23246
|
+
"startedAt",
|
|
23247
|
+
"endedAt",
|
|
23248
|
+
"attempts",
|
|
23249
|
+
"executionTimeMs",
|
|
23250
|
+
"error"
|
|
23251
|
+
];
|
|
23252
|
+
let hasChanged = false;
|
|
23253
|
+
if (!prevTask) {
|
|
23254
|
+
hasChanged = true;
|
|
23255
|
+
} else {
|
|
23256
|
+
for (const field of fieldsToCompare) {
|
|
23257
|
+
if (prevTask[field] !== task[field]) {
|
|
23258
|
+
hasChanged = true;
|
|
23259
|
+
break;
|
|
23260
|
+
}
|
|
23261
|
+
}
|
|
23262
|
+
if (prevTask.tokenUsage !== task.tokenUsage || prevTask.files !== task.files) {
|
|
23263
|
+
hasChanged = true;
|
|
23264
|
+
}
|
|
23265
|
+
}
|
|
23266
|
+
if (!hasChanged) {
|
|
23267
|
+
return prev;
|
|
23268
|
+
}
|
|
23269
|
+
const nextTasks = { ...prev.tasks, [taskId]: task };
|
|
23270
|
+
const taskCount = Object.keys(nextTasks).length;
|
|
23271
|
+
const doneCount = Object.values(nextTasks).filter(
|
|
23272
|
+
(t2) => t2.state === "done"
|
|
23273
|
+
).length;
|
|
23274
|
+
const progress = taskCount > 0 ? doneCount / taskCount * 100 : 0;
|
|
23275
|
+
return {
|
|
23276
|
+
...prev,
|
|
23277
|
+
tasks: nextTasks,
|
|
23278
|
+
doneCount,
|
|
23279
|
+
taskCount,
|
|
23280
|
+
progress,
|
|
23281
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
23282
|
+
};
|
|
23283
|
+
});
|
|
23284
|
+
});
|
|
23285
|
+
return;
|
|
23286
|
+
}
|
|
23175
23287
|
if (type === "state:change") {
|
|
23176
23288
|
const d2 = payload && (payload.data || payload) || {};
|
|
23177
23289
|
if (typeof d2.path === "string" && matchesJobTasksStatusPath(d2.path, jobId)) {
|
|
@@ -23204,12 +23316,14 @@ function useJobDetailWithUpdates(jobId) {
|
|
|
23204
23316
|
const onJobRemoved = (evt) => handleIncomingEvent("job:removed", evt);
|
|
23205
23317
|
const onStatusChanged = (evt) => handleIncomingEvent("status:changed", evt);
|
|
23206
23318
|
const onStateChange = (evt) => handleIncomingEvent("state:change", evt);
|
|
23319
|
+
const onTaskUpdated = (evt) => handleIncomingEvent("task:updated", evt);
|
|
23207
23320
|
es.addEventListener("open", onOpen);
|
|
23208
23321
|
es.addEventListener("job:updated", onJobUpdated);
|
|
23209
23322
|
es.addEventListener("job:created", onJobCreated);
|
|
23210
23323
|
es.addEventListener("job:removed", onJobRemoved);
|
|
23211
23324
|
es.addEventListener("status:changed", onStatusChanged);
|
|
23212
23325
|
es.addEventListener("state:change", onStateChange);
|
|
23326
|
+
es.addEventListener("task:updated", onTaskUpdated);
|
|
23213
23327
|
es.addEventListener("error", onError);
|
|
23214
23328
|
if (es.readyState === 1 && mountedRef.current) {
|
|
23215
23329
|
setConnectionStatus("connected");
|
|
@@ -23224,6 +23338,7 @@ function useJobDetailWithUpdates(jobId) {
|
|
|
23224
23338
|
es.removeEventListener("job:removed", onJobRemoved);
|
|
23225
23339
|
es.removeEventListener("status:changed", onStatusChanged);
|
|
23226
23340
|
es.removeEventListener("state:change", onStateChange);
|
|
23341
|
+
es.removeEventListener("task:updated", onTaskUpdated);
|
|
23227
23342
|
es.removeEventListener("error", onError);
|
|
23228
23343
|
es.close();
|
|
23229
23344
|
} catch (err) {
|