@kb-labs/workflow-daemon 2.118.2 → 2.119.0-canary.cee505c72
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/dist/index.js +25 -103
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { WorkflowEngine, WorkflowService } from '@kb-labs/workflow-engine';
|
|
|
4
4
|
import { getListenOptions, HttpObservabilityCollector, createDaemonServer, createServiceReadyResponse, metricLine } from '@kb-labs/shared-http';
|
|
5
5
|
import { runService } from '@kb-labs/shared-daemon';
|
|
6
6
|
import { logDiagnosticEvent, createContextLogger } from '@kb-labs/core-platform';
|
|
7
|
+
import { isTerminal } from '@kb-labs/workflow-constants';
|
|
7
8
|
import { PluginCronJobSchema, UserCronJobSchema, evaluateExpression, interpolateObject, buildShellSafeCommand, coerceToString, interpolateString } from '@kb-labs/workflow-contracts';
|
|
8
9
|
import { GateHandler } from '@kb-labs/workflow-steps';
|
|
9
10
|
import { SandboxRunner } from '@kb-labs/workflow-runtime';
|
|
@@ -186,7 +187,6 @@ async function createWorkflowWorker(options) {
|
|
|
186
187
|
try {
|
|
187
188
|
const ws = await wsProvider2.materialize({
|
|
188
189
|
workspaceId: wsId,
|
|
189
|
-
sourceRef: "main",
|
|
190
190
|
metadata: { runId: run.id, jobId: job.id },
|
|
191
191
|
onProgress: (event) => {
|
|
192
192
|
jobLogger.info(`[workspace] ${event.stage}: ${event.message}`, {
|
|
@@ -257,6 +257,15 @@ async function createWorkflowWorker(options) {
|
|
|
257
257
|
wasCancelled = true;
|
|
258
258
|
break;
|
|
259
259
|
}
|
|
260
|
+
const freshJob = freshRun?.jobs.find((j) => j.id === job.id);
|
|
261
|
+
if (freshJob && isTerminal(freshJob.status, "job")) {
|
|
262
|
+
jobLogger.warn("[worker] Job already in a terminal state \u2014 aborting in-process execution", {
|
|
263
|
+
runId: run.id,
|
|
264
|
+
jobId: job.id,
|
|
265
|
+
status: freshJob.status
|
|
266
|
+
});
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
260
269
|
const exprCtx = {
|
|
261
270
|
env: freshRun?.env ?? {},
|
|
262
271
|
trigger: freshRun?.trigger ?? { type: "manual" },
|
|
@@ -392,19 +401,13 @@ async function createWorkflowWorker(options) {
|
|
|
392
401
|
if (step.status !== "waiting_approval") {
|
|
393
402
|
await engine.markStepWaitingApproval(run.id, job.id, step.id);
|
|
394
403
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
step
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
stepLogger
|
|
403
|
-
);
|
|
404
|
-
if (approvalResult === "interrupted") {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
continue;
|
|
404
|
+
stepLogger.info("[approval] Step parked awaiting human decision \u2014 releasing worker slot", {
|
|
405
|
+
runId: run.id,
|
|
406
|
+
jobId: job.id,
|
|
407
|
+
stepId: step.id,
|
|
408
|
+
stepName: step.name
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
408
411
|
}
|
|
409
412
|
if (step.spec.uses?.startsWith("workflow:")) {
|
|
410
413
|
await invokeChildWorkflow({
|
|
@@ -833,82 +836,6 @@ function sleep(ms) {
|
|
|
833
836
|
setTimeout(resolve, ms);
|
|
834
837
|
});
|
|
835
838
|
}
|
|
836
|
-
async function waitForApproval(engine, runId, jobId, step, interpolatedWith, isStopRequested, stepLogger) {
|
|
837
|
-
const approvalTimeoutMs = interpolatedWith?.["timeoutMs"];
|
|
838
|
-
if (!approvalTimeoutMs) {
|
|
839
|
-
stepLogger.warn(
|
|
840
|
-
"[approval] No timeout configured \u2014 approval may wait indefinitely",
|
|
841
|
-
{
|
|
842
|
-
runId,
|
|
843
|
-
jobId,
|
|
844
|
-
stepId: step.id,
|
|
845
|
-
stepName: step.name
|
|
846
|
-
}
|
|
847
|
-
);
|
|
848
|
-
}
|
|
849
|
-
stepLogger.info("[approval] Waiting for approval", {
|
|
850
|
-
runId,
|
|
851
|
-
jobId,
|
|
852
|
-
stepId: step.id,
|
|
853
|
-
stepName: step.name,
|
|
854
|
-
context: interpolatedWith
|
|
855
|
-
});
|
|
856
|
-
const approvalStartMs = Date.now();
|
|
857
|
-
let pollCount = 0;
|
|
858
|
-
while (!isStopRequested()) {
|
|
859
|
-
await sleep(2e3);
|
|
860
|
-
pollCount++;
|
|
861
|
-
const currentRun = await engine.getRun(runId);
|
|
862
|
-
if (currentRun?.status === "cancelled") {
|
|
863
|
-
stepLogger.info(
|
|
864
|
-
"[approval] Run cancelled \u2014 treating as interrupted, NOT approved",
|
|
865
|
-
{
|
|
866
|
-
runId,
|
|
867
|
-
stepId: step.id,
|
|
868
|
-
stepName: step.name,
|
|
869
|
-
waitedMs: Date.now() - approvalStartMs
|
|
870
|
-
}
|
|
871
|
-
);
|
|
872
|
-
return "interrupted";
|
|
873
|
-
}
|
|
874
|
-
const currentJob = currentRun?.jobs.find((j) => j.id === jobId);
|
|
875
|
-
const currentStep = currentJob?.steps.find((s) => s.id === step.id);
|
|
876
|
-
if (!currentStep || currentStep.status === "success") {
|
|
877
|
-
stepLogger.info("[approval] Approval granted", {
|
|
878
|
-
runId,
|
|
879
|
-
stepId: step.id,
|
|
880
|
-
stepName: step.name,
|
|
881
|
-
waitedMs: Date.now() - approvalStartMs
|
|
882
|
-
});
|
|
883
|
-
break;
|
|
884
|
-
}
|
|
885
|
-
if (currentStep.status === "failed") {
|
|
886
|
-
stepLogger.info("[approval] Approval rejected", {
|
|
887
|
-
runId,
|
|
888
|
-
stepId: step.id,
|
|
889
|
-
stepName: step.name,
|
|
890
|
-
waitedMs: Date.now() - approvalStartMs
|
|
891
|
-
});
|
|
892
|
-
break;
|
|
893
|
-
}
|
|
894
|
-
if (pollCount % 10 === 0) {
|
|
895
|
-
stepLogger.info("[approval] Still waiting for approval", {
|
|
896
|
-
runId,
|
|
897
|
-
stepId: step.id,
|
|
898
|
-
stepName: step.name,
|
|
899
|
-
waitedMs: Date.now() - approvalStartMs,
|
|
900
|
-
pollCount
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (isStopRequested()) {
|
|
905
|
-
stepLogger.info("Approval wait interrupted by shutdown", {
|
|
906
|
-
stepId: step.id
|
|
907
|
-
});
|
|
908
|
-
return "interrupted";
|
|
909
|
-
}
|
|
910
|
-
return "done";
|
|
911
|
-
}
|
|
912
839
|
async function applyGateSkip(engine, run, job, step, decision, stepLogger) {
|
|
913
840
|
const { skipTo, outputs } = decision;
|
|
914
841
|
stepLogger.info("[gate] Gate triggered skip-forward", {
|
|
@@ -932,8 +859,7 @@ async function applyGateSkip(engine, run, job, step, decision, stepLogger) {
|
|
|
932
859
|
if (s.spec.id === skipTo || s.id === skipTo) {
|
|
933
860
|
break;
|
|
934
861
|
}
|
|
935
|
-
await stateStore.
|
|
936
|
-
draft.status = "success";
|
|
862
|
+
await stateStore.transitionStep(run.id, job.id, s.id, "success", (draft) => {
|
|
937
863
|
draft.outputs = { skipped: true };
|
|
938
864
|
draft.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
939
865
|
draft.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1005,21 +931,19 @@ async function applyGateRestart(engine, run, job, step, decision, stepLogger) {
|
|
|
1005
931
|
}
|
|
1006
932
|
const pending = (rj.needs ?? []).filter((n) => resetNames.has(n));
|
|
1007
933
|
for (const s of rj.steps) {
|
|
1008
|
-
await stateStore.
|
|
1009
|
-
draft.status = "queued";
|
|
934
|
+
await stateStore.transitionStep(run.id, rj.id, s.id, "queued", (draft) => {
|
|
1010
935
|
draft.startedAt = void 0;
|
|
1011
936
|
draft.finishedAt = void 0;
|
|
1012
937
|
draft.error = void 0;
|
|
1013
938
|
draft.outputs = void 0;
|
|
1014
|
-
});
|
|
939
|
+
}, { allowReset: true });
|
|
1015
940
|
}
|
|
1016
|
-
await stateStore.
|
|
1017
|
-
draft.status = "queued";
|
|
941
|
+
await stateStore.transitionJob(run.id, rj.id, "queued", (draft) => {
|
|
1018
942
|
draft.startedAt = void 0;
|
|
1019
943
|
draft.finishedAt = void 0;
|
|
1020
944
|
draft.pendingDependencies = [...pending];
|
|
1021
945
|
draft.blocked = pending.length > 0;
|
|
1022
|
-
});
|
|
946
|
+
}, { allowReset: true });
|
|
1023
947
|
}
|
|
1024
948
|
const refreshed = await engine.getRun(run.id);
|
|
1025
949
|
const targetJob = refreshed?.jobs.find((j) => j.jobName === restartFrom);
|
|
@@ -1045,17 +969,15 @@ async function applyGateRestart(engine, run, job, step, decision, stepLogger) {
|
|
|
1045
969
|
foundTarget = true;
|
|
1046
970
|
}
|
|
1047
971
|
if (foundTarget) {
|
|
1048
|
-
await stateStore.
|
|
1049
|
-
draft.status = "queued";
|
|
972
|
+
await stateStore.transitionStep(run.id, job.id, s.id, "queued", (draft) => {
|
|
1050
973
|
draft.startedAt = void 0;
|
|
1051
974
|
draft.finishedAt = void 0;
|
|
1052
975
|
draft.error = void 0;
|
|
1053
976
|
draft.outputs = void 0;
|
|
1054
|
-
});
|
|
977
|
+
}, { allowReset: true });
|
|
1055
978
|
}
|
|
1056
979
|
}
|
|
1057
|
-
await stateStore.
|
|
1058
|
-
draft.status = "queued";
|
|
980
|
+
await stateStore.transitionJob(run.id, job.id, "queued", (draft) => {
|
|
1059
981
|
draft.startedAt = void 0;
|
|
1060
982
|
draft.finishedAt = void 0;
|
|
1061
983
|
});
|