@smithers-orchestrator/engine 0.20.1 → 0.20.4
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 +14 -15
- package/src/approvals.js +6 -0
- package/src/child-workflow.js +7 -0
- package/src/effect/builder.js +48 -13
- package/src/effect/compute-task-bridge.js +42 -47
- package/src/effect/deferred-state-bridge.js +37 -0
- package/src/effect/diff-bundle.js +24 -12
- package/src/effect/durable-deferred-bridge.js +16 -4
- package/src/effect/http-runner.js +2 -86
- package/src/effect/single-runner.js +26 -1
- package/src/effect/sql-message-storage.js +2 -817
- package/src/effect/static-task-bridge.js +6 -2
- package/src/effect/workflow-bridge.js +15 -4
- package/src/effect/workflow-make-bridge.js +10 -3
- package/src/engine.js +85 -8
- package/src/hot/HotWorkflowController.js +37 -23
- package/src/hot/overlay.js +42 -24
- package/src/human-requests.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithers-orchestrator/engine",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.4",
|
|
4
4
|
"description": "Concrete Smithers workflow execution engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -33,20 +33,19 @@
|
|
|
33
33
|
"react": "^19.2.5",
|
|
34
34
|
"react-dom": "^19.2.5",
|
|
35
35
|
"zod": "^4.3.6",
|
|
36
|
-
"@smithers-orchestrator/components": "0.20.
|
|
37
|
-
"@smithers-orchestrator/
|
|
38
|
-
"@smithers-orchestrator/driver": "0.20.
|
|
39
|
-
"@smithers-orchestrator/errors": "0.20.
|
|
40
|
-
"@smithers-orchestrator/
|
|
41
|
-
"@smithers-orchestrator/
|
|
42
|
-
"@smithers-orchestrator/
|
|
43
|
-
"@smithers-orchestrator/
|
|
44
|
-
"@smithers-orchestrator/
|
|
45
|
-
"@smithers-orchestrator/
|
|
46
|
-
"@smithers-orchestrator/
|
|
47
|
-
"@smithers-orchestrator/
|
|
48
|
-
"@smithers-orchestrator/
|
|
49
|
-
"@smithers-orchestrator/vcs": "0.20.1"
|
|
36
|
+
"@smithers-orchestrator/components": "0.20.4",
|
|
37
|
+
"@smithers-orchestrator/agents": "0.20.4",
|
|
38
|
+
"@smithers-orchestrator/driver": "0.20.4",
|
|
39
|
+
"@smithers-orchestrator/errors": "0.20.4",
|
|
40
|
+
"@smithers-orchestrator/graph": "0.20.4",
|
|
41
|
+
"@smithers-orchestrator/observability": "0.20.4",
|
|
42
|
+
"@smithers-orchestrator/sandbox": "0.20.4",
|
|
43
|
+
"@smithers-orchestrator/react-reconciler": "0.20.4",
|
|
44
|
+
"@smithers-orchestrator/scheduler": "0.20.4",
|
|
45
|
+
"@smithers-orchestrator/db": "0.20.4",
|
|
46
|
+
"@smithers-orchestrator/scorers": "0.20.4",
|
|
47
|
+
"@smithers-orchestrator/time-travel": "0.20.4",
|
|
48
|
+
"@smithers-orchestrator/vcs": "0.20.4"
|
|
50
49
|
},
|
|
51
50
|
"devDependencies": {
|
|
52
51
|
"@types/bun": "latest",
|
package/src/approvals.js
CHANGED
|
@@ -217,3 +217,9 @@ export function denyNode(adapter, runId, nodeId, iteration, note, decidedBy, dec
|
|
|
217
217
|
approvalDecidedBy: decidedBy ?? null,
|
|
218
218
|
}), Effect.withLogSpan("approval:deny"));
|
|
219
219
|
}
|
|
220
|
+
export const __approvalInternals = {
|
|
221
|
+
isAsyncApprovalRequest,
|
|
222
|
+
nextRunStatusForApproval,
|
|
223
|
+
serializeDecision,
|
|
224
|
+
validateNodeWaitingForApproval,
|
|
225
|
+
};
|
package/src/child-workflow.js
CHANGED
|
@@ -161,3 +161,10 @@ export async function executeChildWorkflow(parentWorkflow, options) {
|
|
|
161
161
|
output: normalizeChildOutput(result),
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
+
export const __childWorkflowInternals = {
|
|
165
|
+
buildChildWorkflowRunId,
|
|
166
|
+
normalizeChildInput,
|
|
167
|
+
normalizeChildOutput,
|
|
168
|
+
resolveChildWorkflow,
|
|
169
|
+
stripSystemColumns,
|
|
170
|
+
};
|
package/src/effect/builder.js
CHANGED
|
@@ -289,21 +289,16 @@ function deriveRetryCount(retry) {
|
|
|
289
289
|
return Math.max(0, Math.floor(maxAttempts - 1));
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return count;
|
|
299
|
-
}
|
|
300
|
-
count += 1;
|
|
292
|
+
const driver = Effect.runSync(Schedule.driver(retry));
|
|
293
|
+
let count = 0;
|
|
294
|
+
while (count < 100) {
|
|
295
|
+
const exit = Effect.runSyncExit(driver.next(undefined));
|
|
296
|
+
if (Exit.isFailure(exit)) {
|
|
297
|
+
return count;
|
|
301
298
|
}
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
catch {
|
|
305
|
-
return 0;
|
|
299
|
+
count += 1;
|
|
306
300
|
}
|
|
301
|
+
return count;
|
|
307
302
|
}
|
|
308
303
|
/**
|
|
309
304
|
* @template T
|
|
@@ -1101,3 +1096,43 @@ export const Smithers = {
|
|
|
1101
1096
|
workflow,
|
|
1102
1097
|
fragment,
|
|
1103
1098
|
};
|
|
1099
|
+
|
|
1100
|
+
export const __builderInternals = {
|
|
1101
|
+
ApprovalDecision,
|
|
1102
|
+
SmithersSqlite,
|
|
1103
|
+
annotateLoops,
|
|
1104
|
+
applyPrefixId,
|
|
1105
|
+
assertUniqueHandleIds,
|
|
1106
|
+
buildNeedsContext,
|
|
1107
|
+
buildUserContext,
|
|
1108
|
+
collectHandles,
|
|
1109
|
+
compileGraph,
|
|
1110
|
+
compileNeeds,
|
|
1111
|
+
createBuilder,
|
|
1112
|
+
createBuilderDb,
|
|
1113
|
+
createInputTable,
|
|
1114
|
+
createPayloadTable,
|
|
1115
|
+
decodeSchema,
|
|
1116
|
+
deriveRetryCount,
|
|
1117
|
+
deriveRetryPolicy,
|
|
1118
|
+
durationToMs,
|
|
1119
|
+
encodeSchema,
|
|
1120
|
+
evaluateSkip,
|
|
1121
|
+
executeStepHandle,
|
|
1122
|
+
extractResult,
|
|
1123
|
+
isBuilderNode,
|
|
1124
|
+
isWorkflowGraph,
|
|
1125
|
+
makeFactory,
|
|
1126
|
+
makeGraph,
|
|
1127
|
+
makeTableName,
|
|
1128
|
+
normalizeExecutionError,
|
|
1129
|
+
readHandle,
|
|
1130
|
+
readHandleMaybe,
|
|
1131
|
+
readLatestHandleResult,
|
|
1132
|
+
renderNode,
|
|
1133
|
+
resolveEffectResult,
|
|
1134
|
+
resolveHandleIteration,
|
|
1135
|
+
sanitizeIdentifier,
|
|
1136
|
+
sqlite,
|
|
1137
|
+
stripPersistedKeys,
|
|
1138
|
+
};
|
|
@@ -38,13 +38,13 @@ function isAbortError(err) {
|
|
|
38
38
|
}
|
|
39
39
|
if (fromTaggedError(err)?.code === "TASK_ABORTED")
|
|
40
40
|
return true;
|
|
41
|
-
if (err.name === "AbortError")
|
|
42
|
-
return true;
|
|
43
41
|
if (typeof DOMException !== "undefined" &&
|
|
44
42
|
err instanceof DOMException &&
|
|
45
43
|
err.name === "AbortError") {
|
|
46
44
|
return true;
|
|
47
45
|
}
|
|
46
|
+
if (err.name === "AbortError")
|
|
47
|
+
return true;
|
|
48
48
|
if (err instanceof Error) {
|
|
49
49
|
return /aborted|abort/i.test(err.message);
|
|
50
50
|
}
|
|
@@ -90,9 +90,6 @@ function validateHeartbeatValue(value, path, seen) {
|
|
|
90
90
|
typeof value === "symbol") {
|
|
91
91
|
throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", `Heartbeat payload contains a non-JSON value (invalid at ${path}).`, { path, valueType: typeof value });
|
|
92
92
|
}
|
|
93
|
-
if (typeof value !== "object") {
|
|
94
|
-
throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", `Heartbeat payload contains an unsupported value at ${path}.`, { path });
|
|
95
|
-
}
|
|
96
93
|
if (seen.has(value)) {
|
|
97
94
|
throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", "Heartbeat payload cannot contain circular references.", { path });
|
|
98
95
|
}
|
|
@@ -171,6 +168,25 @@ function isHeartbeatPayloadValidationError(err) {
|
|
|
171
168
|
return (code === "HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE" ||
|
|
172
169
|
code === "HEARTBEAT_PAYLOAD_TOO_LARGE");
|
|
173
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* @param {AbortController} controller
|
|
173
|
+
* @param {AbortSignal} signal
|
|
174
|
+
* @returns {() => void}
|
|
175
|
+
*/
|
|
176
|
+
function linkEffectAbortSignal(controller, signal) {
|
|
177
|
+
const forwardAbort = () => {
|
|
178
|
+
controller.abort(signal.reason ?? makeAbortError());
|
|
179
|
+
};
|
|
180
|
+
if (signal.aborted) {
|
|
181
|
+
forwardAbort();
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
signal.addEventListener("abort", forwardAbort, { once: true });
|
|
185
|
+
}
|
|
186
|
+
return () => {
|
|
187
|
+
signal.removeEventListener("abort", forwardAbort);
|
|
188
|
+
};
|
|
189
|
+
}
|
|
174
190
|
/**
|
|
175
191
|
* @param {_TaskDescriptor} desc
|
|
176
192
|
* @param {boolean} cacheEnabled
|
|
@@ -288,46 +304,26 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
|
|
|
288
304
|
finally {
|
|
289
305
|
heartbeatWriteInFlight = false;
|
|
290
306
|
if (heartbeatHasPendingWrite && !heartbeatClosed) {
|
|
291
|
-
if (heartbeatWriteTimer) {
|
|
292
|
-
clearTimeout(heartbeatWriteTimer);
|
|
293
|
-
heartbeatWriteTimer = undefined;
|
|
294
|
-
}
|
|
295
307
|
void flushHeartbeat();
|
|
296
308
|
}
|
|
297
309
|
}
|
|
298
310
|
};
|
|
299
311
|
/**
|
|
300
312
|
* @param {unknown} data
|
|
301
|
-
* @param {{ internal?: boolean }} [opts]
|
|
302
313
|
*/
|
|
303
|
-
const queueHeartbeat = (data
|
|
314
|
+
const queueHeartbeat = (data) => {
|
|
304
315
|
if (taskCompleted ||
|
|
305
316
|
heartbeatClosed ||
|
|
306
|
-
|
|
317
|
+
taskExecutionReturned) {
|
|
307
318
|
return;
|
|
308
319
|
}
|
|
309
320
|
const heartbeatAtMs = nowMs();
|
|
310
321
|
let nextHeartbeatDataJson = null;
|
|
311
322
|
let dataSizeBytes = 0;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
dataSizeBytes = serialized.dataSizeBytes;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
if (!opts?.internal) {
|
|
321
|
-
throw error;
|
|
322
|
-
}
|
|
323
|
-
logWarning("internal heartbeat payload rejected", {
|
|
324
|
-
runId,
|
|
325
|
-
nodeId: desc.nodeId,
|
|
326
|
-
iteration: desc.iteration,
|
|
327
|
-
attempt: attemptNo,
|
|
328
|
-
error: error instanceof Error ? error.message : String(error),
|
|
329
|
-
}, "heartbeat:record");
|
|
330
|
-
return;
|
|
323
|
+
if (data !== undefined) {
|
|
324
|
+
const serialized = serializeHeartbeatPayload(data);
|
|
325
|
+
nextHeartbeatDataJson = serialized.heartbeatDataJson;
|
|
326
|
+
dataSizeBytes = serialized.dataSizeBytes;
|
|
331
327
|
}
|
|
332
328
|
heartbeatPendingAtMs = heartbeatAtMs;
|
|
333
329
|
heartbeatPendingDataJson = nextHeartbeatDataJson;
|
|
@@ -358,10 +354,6 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
|
|
|
358
354
|
if (Exit.isSuccess(exit)) {
|
|
359
355
|
return exit.value;
|
|
360
356
|
}
|
|
361
|
-
const failure = Cause.failureOption(exit.cause);
|
|
362
|
-
if (failure._tag === "Some") {
|
|
363
|
-
throw failure.value;
|
|
364
|
-
}
|
|
365
357
|
throw Cause.squash(exit.cause);
|
|
366
358
|
};
|
|
367
359
|
const heartbeatTimeoutMs = desc.heartbeatTimeoutMs;
|
|
@@ -484,17 +476,7 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
|
|
|
484
476
|
try: (effectSignal) => {
|
|
485
477
|
const computeAbortController = new AbortController();
|
|
486
478
|
const removeTaskAbortForwarder = wireAbortSignal(computeAbortController, taskSignal);
|
|
487
|
-
const
|
|
488
|
-
computeAbortController.abort(effectSignal.reason ?? makeAbortError());
|
|
489
|
-
};
|
|
490
|
-
if (effectSignal.aborted) {
|
|
491
|
-
forwardEffectAbort();
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
effectSignal.addEventListener("abort", forwardEffectAbort, {
|
|
495
|
-
once: true,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
479
|
+
const removeEffectAbortForwarder = linkEffectAbortSignal(computeAbortController, effectSignal);
|
|
498
480
|
return Promise.resolve()
|
|
499
481
|
.then(() => withTaskRuntime({
|
|
500
482
|
runId,
|
|
@@ -510,7 +492,7 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
|
|
|
510
492
|
}, () => desc.computeFn()))
|
|
511
493
|
.finally(() => {
|
|
512
494
|
removeTaskAbortForwarder();
|
|
513
|
-
|
|
495
|
+
removeEffectAbortForwarder();
|
|
514
496
|
});
|
|
515
497
|
},
|
|
516
498
|
catch: (error) => error,
|
|
@@ -738,3 +720,16 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
|
|
|
738
720
|
removeAbortForwarder();
|
|
739
721
|
}
|
|
740
722
|
};
|
|
723
|
+
|
|
724
|
+
export const __computeTaskBridgeInternals = {
|
|
725
|
+
TASK_HEARTBEAT_MAX_PAYLOAD_BYTES,
|
|
726
|
+
TASK_HEARTBEAT_THROTTLE_MS,
|
|
727
|
+
TASK_HEARTBEAT_TIMEOUT_CHECK_MS,
|
|
728
|
+
heartbeatTimeoutReasonFromAbort,
|
|
729
|
+
isAbortError,
|
|
730
|
+
isHeartbeatPayloadValidationError,
|
|
731
|
+
linkEffectAbortSignal,
|
|
732
|
+
parseAttemptHeartbeatData,
|
|
733
|
+
serializeHeartbeatPayload,
|
|
734
|
+
validateHeartbeatValue,
|
|
735
|
+
};
|
|
@@ -1340,3 +1340,40 @@ export async function cancelPendingTimersBridge(adapter, runId, eventBus, reason
|
|
|
1340
1340
|
}));
|
|
1341
1341
|
}
|
|
1342
1342
|
}
|
|
1343
|
+
|
|
1344
|
+
export const __deferredStateBridgeInternals = {
|
|
1345
|
+
buildApprovalRequestJson,
|
|
1346
|
+
buildHumanRequestSchemaJson,
|
|
1347
|
+
buildTimerAttemptMeta,
|
|
1348
|
+
buildTimerSnapshot,
|
|
1349
|
+
buildWaitForEventAttemptMeta,
|
|
1350
|
+
buildWaitForEventSnapshot,
|
|
1351
|
+
cancelPendingTimersBridge,
|
|
1352
|
+
defaultAutoApprovalDecision,
|
|
1353
|
+
ensurePendingHumanRequest,
|
|
1354
|
+
failWaitForEventTaskBridge,
|
|
1355
|
+
finishWaitForEventTaskBridge,
|
|
1356
|
+
getHumanTaskPrompt,
|
|
1357
|
+
parseAttemptErrorCode,
|
|
1358
|
+
parseOptionalFiniteNumber,
|
|
1359
|
+
parseTimerDurationMs,
|
|
1360
|
+
parseTimerSnapshot,
|
|
1361
|
+
parseTimerType,
|
|
1362
|
+
parseTimerUntilMs,
|
|
1363
|
+
parseWaitForEventCorrelationId,
|
|
1364
|
+
parseWaitForEventOnTimeout,
|
|
1365
|
+
parseWaitForEventSignalName,
|
|
1366
|
+
parseWaitForEventSnapshot,
|
|
1367
|
+
reconcileHumanRequestValidationFailure,
|
|
1368
|
+
renderHumanPromptToText,
|
|
1369
|
+
resolveApprovalTaskStateBridge,
|
|
1370
|
+
resolveTimerTaskStateBridge,
|
|
1371
|
+
resolveWaitForEventTaskStateBridge,
|
|
1372
|
+
resolveWaitForEventTimeoutBridge,
|
|
1373
|
+
shouldAutoApprove,
|
|
1374
|
+
shouldClearAsyncWaitMetric,
|
|
1375
|
+
syncApprovalDurableDeferredFromDb,
|
|
1376
|
+
syncWaitForEventDurableDeferredFromDb,
|
|
1377
|
+
updateAsyncExternalWaitPendingSafe,
|
|
1378
|
+
validateDeferredOutputPayload,
|
|
1379
|
+
};
|
|
@@ -171,6 +171,24 @@ async function computeUntrackedDiffs(currentDir) {
|
|
|
171
171
|
}
|
|
172
172
|
return diffs;
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* @param {string} currentDir
|
|
176
|
+
* @param {string} targetRef
|
|
177
|
+
* @param {string} path
|
|
178
|
+
* @returns {Promise<string | undefined>}
|
|
179
|
+
*/
|
|
180
|
+
async function readBinaryContentAtRef(currentDir, targetRef, path) {
|
|
181
|
+
try {
|
|
182
|
+
const { stdout } = await runGit(currentDir, [
|
|
183
|
+
"show",
|
|
184
|
+
`${targetRef}:${path}`,
|
|
185
|
+
]);
|
|
186
|
+
return Buffer.from(stdout, "binary").toString("base64");
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
174
192
|
/**
|
|
175
193
|
* Compute a diff bundle strictly between two immutable refs.
|
|
176
194
|
*
|
|
@@ -225,18 +243,7 @@ export async function computeDiffBundleBetweenRefs(baseRef, targetRef, currentDi
|
|
|
225
243
|
const binary = isBinaryPatch(chunk) || binaryPaths.has(path);
|
|
226
244
|
let binaryContent;
|
|
227
245
|
if (binary && operation !== "delete") {
|
|
228
|
-
|
|
229
|
-
const { stdout } = await runGit(currentDir, [
|
|
230
|
-
"show",
|
|
231
|
-
`${targetRef}:${path}`,
|
|
232
|
-
]);
|
|
233
|
-
binaryContent = Buffer.from(stdout, "binary").toString("base64");
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
// File may not be readable as a blob at targetRef; fall through
|
|
237
|
-
// without binaryContent. Caller receives operation + diff only.
|
|
238
|
-
binaryContent = undefined;
|
|
239
|
-
}
|
|
246
|
+
binaryContent = await readBinaryContentAtRef(currentDir, targetRef, path);
|
|
240
247
|
}
|
|
241
248
|
patches.push({
|
|
242
249
|
path,
|
|
@@ -350,3 +357,8 @@ export async function applyDiffBundle(bundle, targetDir) {
|
|
|
350
357
|
}
|
|
351
358
|
}
|
|
352
359
|
}
|
|
360
|
+
export const __diffBundleInternals = {
|
|
361
|
+
extractPatchPath,
|
|
362
|
+
readBinaryContentAtRef,
|
|
363
|
+
splitGitDiff,
|
|
364
|
+
};
|
|
@@ -135,6 +135,12 @@ function buildResolvedWaitForEventMetaJson(snapshot, signal) {
|
|
|
135
135
|
},
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
+
async function decrementAsyncEventWaitPending(updatePending = updateAsyncExternalWaitPending) {
|
|
139
|
+
try {
|
|
140
|
+
await Effect.runPromise(updatePending("event", -1));
|
|
141
|
+
}
|
|
142
|
+
catch { }
|
|
143
|
+
}
|
|
138
144
|
/**
|
|
139
145
|
* @param {_SmithersDb} adapter
|
|
140
146
|
* @param {string} runId
|
|
@@ -154,10 +160,7 @@ async function markWaitForEventResolved(adapter, runId, nodeId, iteration, signa
|
|
|
154
160
|
metaJson: buildResolvedWaitForEventMetaJson(snapshot, signal),
|
|
155
161
|
}));
|
|
156
162
|
if (snapshot.waitAsync) {
|
|
157
|
-
|
|
158
|
-
await Effect.runPromise(updateAsyncExternalWaitPending("event", -1));
|
|
159
|
-
}
|
|
160
|
-
catch { }
|
|
163
|
+
await decrementAsyncEventWaitPending();
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
const deferredResolutions = new Map();
|
|
@@ -280,3 +283,12 @@ export const bridgeSignalResolve = async (adapter, runId, signal) => {
|
|
|
280
283
|
await bridgeWaitForEventResolve(adapter, runId, node.nodeId, iteration, signal);
|
|
281
284
|
}
|
|
282
285
|
};
|
|
286
|
+
export const __durableDeferredBridgeInternals = {
|
|
287
|
+
buildResolvedWaitForEventMetaJson,
|
|
288
|
+
decrementAsyncEventWaitPending,
|
|
289
|
+
getAdapterNamespace,
|
|
290
|
+
markWaitForEventResolved,
|
|
291
|
+
normalizeCorrelationId,
|
|
292
|
+
parseOptionalFiniteNumber,
|
|
293
|
+
parseWaitForEventAttemptSnapshot,
|
|
294
|
+
};
|
|
@@ -1,86 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { Effect, Layer } from "effect";
|
|
5
|
-
import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
6
|
-
import { spawnCaptureEffect } from "@smithers-orchestrator/driver/child-process";
|
|
7
|
-
import { toSmithersError } from "@smithers-orchestrator/errors/toSmithersError";
|
|
8
|
-
import { SandboxEntityExecutor } from "@smithers-orchestrator/sandbox/effect/sandbox-entity";
|
|
9
|
-
/**
|
|
10
|
-
* @param {SandboxTransportConfig} config
|
|
11
|
-
* @returns {SandboxHandle}
|
|
12
|
-
*/
|
|
13
|
-
function baseHandle(config) {
|
|
14
|
-
const sandboxRoot = join(config.rootDir, ".smithers", "sandboxes", config.runId, config.sandboxId);
|
|
15
|
-
return {
|
|
16
|
-
runtime: config.runtime,
|
|
17
|
-
runId: config.runId,
|
|
18
|
-
sandboxId: config.sandboxId,
|
|
19
|
-
sandboxRoot,
|
|
20
|
-
requestPath: join(sandboxRoot, "request"),
|
|
21
|
-
resultPath: join(sandboxRoot, "result"),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
export const DockerSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, SandboxEntityExecutor.of({
|
|
25
|
-
create: (config) => Effect.gen(function* () {
|
|
26
|
-
const handle = baseHandle(config);
|
|
27
|
-
yield* spawnCaptureEffect("docker", ["info"], {
|
|
28
|
-
cwd: config.rootDir,
|
|
29
|
-
env: process.env,
|
|
30
|
-
timeoutMs: 10_000,
|
|
31
|
-
maxOutputBytes: 200_000,
|
|
32
|
-
}).pipe(Effect.catchAll(() => Effect.fail(new SmithersError("PROCESS_SPAWN_FAILED", "Docker daemon not reachable.", { runtime: "docker" }))));
|
|
33
|
-
yield* Effect.tryPromise({
|
|
34
|
-
try: async () => {
|
|
35
|
-
await mkdir(handle.requestPath, { recursive: true });
|
|
36
|
-
await mkdir(handle.resultPath, { recursive: true });
|
|
37
|
-
},
|
|
38
|
-
catch: (cause) => toSmithersError(cause, "create docker sandbox workspace"),
|
|
39
|
-
});
|
|
40
|
-
return handle;
|
|
41
|
-
}),
|
|
42
|
-
ship: (bundlePath, handle) => Effect.tryPromise({
|
|
43
|
-
try: async () => {
|
|
44
|
-
await rm(handle.requestPath, { recursive: true, force: true });
|
|
45
|
-
await mkdir(handle.requestPath, { recursive: true });
|
|
46
|
-
await cp(bundlePath, handle.requestPath, { recursive: true });
|
|
47
|
-
},
|
|
48
|
-
catch: (cause) => toSmithersError(cause, "ship docker bundle"),
|
|
49
|
-
}),
|
|
50
|
-
execute: (_command, _handle) => Effect.succeed({ exitCode: 0 }),
|
|
51
|
-
collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
|
|
52
|
-
cleanup: (_handle) => Effect.void,
|
|
53
|
-
}));
|
|
54
|
-
export const CodeplaneSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, SandboxEntityExecutor.of({
|
|
55
|
-
create: (config) => Effect.gen(function* () {
|
|
56
|
-
const apiUrl = process.env.CODEPLANE_API_URL;
|
|
57
|
-
const apiKey = process.env.CODEPLANE_API_KEY;
|
|
58
|
-
if (!apiUrl || !apiKey) {
|
|
59
|
-
yield* Effect.fail(new SmithersError("INVALID_INPUT", "Codeplane runtime requires CODEPLANE_API_URL and CODEPLANE_API_KEY."));
|
|
60
|
-
}
|
|
61
|
-
const handle = baseHandle(config);
|
|
62
|
-
yield* Effect.tryPromise({
|
|
63
|
-
try: async () => {
|
|
64
|
-
await mkdir(handle.requestPath, { recursive: true });
|
|
65
|
-
await mkdir(handle.resultPath, { recursive: true });
|
|
66
|
-
},
|
|
67
|
-
catch: (cause) => toSmithersError(cause, "create codeplane sandbox workspace"),
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
...handle,
|
|
71
|
-
workspaceId: `${config.runId}:${config.sandboxId}`,
|
|
72
|
-
};
|
|
73
|
-
}),
|
|
74
|
-
ship: (bundlePath, handle) => Effect.tryPromise({
|
|
75
|
-
try: async () => {
|
|
76
|
-
await rm(handle.requestPath, { recursive: true, force: true });
|
|
77
|
-
await mkdir(handle.requestPath, { recursive: true });
|
|
78
|
-
await cp(bundlePath, handle.requestPath, { recursive: true });
|
|
79
|
-
},
|
|
80
|
-
catch: (cause) => toSmithersError(cause, "ship codeplane bundle"),
|
|
81
|
-
}),
|
|
82
|
-
execute: (_command, _handle) => Effect.succeed({ exitCode: 0 }),
|
|
83
|
-
collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
|
|
84
|
-
cleanup: (_handle) => Effect.void,
|
|
85
|
-
}));
|
|
86
|
-
export const SandboxHttpRunner = HttpRunner;
|
|
1
|
+
// Preserve the engine subpath while using the canonical sandbox implementation.
|
|
2
|
+
export * from "@smithers-orchestrator/sandbox/effect/http-runner";
|
|
@@ -142,8 +142,15 @@ async function buildSingleRunnerRuntime() {
|
|
|
142
142
|
* @returns {Promise<SingleRunnerRuntime>}
|
|
143
143
|
*/
|
|
144
144
|
async function getSingleRunnerRuntime() {
|
|
145
|
+
return getSingleRunnerRuntimeFromBuilder(buildSingleRunnerRuntime);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @param {() => Promise<SingleRunnerRuntime>} buildRuntime
|
|
149
|
+
* @returns {Promise<SingleRunnerRuntime>}
|
|
150
|
+
*/
|
|
151
|
+
async function getSingleRunnerRuntimeFromBuilder(buildRuntime) {
|
|
145
152
|
if (!singleRunnerRuntimePromise) {
|
|
146
|
-
singleRunnerRuntimePromise =
|
|
153
|
+
singleRunnerRuntimePromise = buildRuntime().catch((error) => {
|
|
147
154
|
singleRunnerRuntimePromise = undefined;
|
|
148
155
|
throw error;
|
|
149
156
|
});
|
|
@@ -187,3 +194,21 @@ export function subscribeTaskWorkerDispatches(subscriber) {
|
|
|
187
194
|
dispatchSubscribers.delete(subscriber);
|
|
188
195
|
};
|
|
189
196
|
}
|
|
197
|
+
|
|
198
|
+
export const __singleRunnerInternals = {
|
|
199
|
+
buildMissingExecutionResult,
|
|
200
|
+
buildSingleRunnerRuntime,
|
|
201
|
+
consumeWorkerError,
|
|
202
|
+
dispatchSubscribers,
|
|
203
|
+
getSingleRunnerRuntimeFromBuilder,
|
|
204
|
+
getSingleRunnerRuntime,
|
|
205
|
+
setSingleRunnerRuntimePromiseForTest: (promise) => {
|
|
206
|
+
singleRunnerRuntimePromise = promise;
|
|
207
|
+
},
|
|
208
|
+
notifyDispatchSubscribers,
|
|
209
|
+
runRegisteredExecution,
|
|
210
|
+
storeWorkerError,
|
|
211
|
+
toWorkerTaskError,
|
|
212
|
+
workerErrors,
|
|
213
|
+
workerExecutions,
|
|
214
|
+
};
|