@a5c-ai/babysitter-sdk 0.0.16

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.
Files changed (169) hide show
  1. package/dist/cli/main.d.ts +5 -0
  2. package/dist/cli/main.d.ts.map +1 -0
  3. package/dist/cli/main.js +1343 -0
  4. package/dist/cli/nodeTaskRunner.d.ts +16 -0
  5. package/dist/cli/nodeTaskRunner.d.ts.map +1 -0
  6. package/dist/cli/nodeTaskRunner.js +46 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +25 -0
  10. package/dist/runner/env.d.ts +58 -0
  11. package/dist/runner/env.d.ts.map +1 -0
  12. package/dist/runner/env.js +113 -0
  13. package/dist/runner/index.d.ts +3 -0
  14. package/dist/runner/index.d.ts.map +1 -0
  15. package/dist/runner/index.js +18 -0
  16. package/dist/runner/nodeRunner.d.ts +60 -0
  17. package/dist/runner/nodeRunner.d.ts.map +1 -0
  18. package/dist/runner/nodeRunner.js +354 -0
  19. package/dist/runtime/commitEffectResult.d.ts +3 -0
  20. package/dist/runtime/commitEffectResult.d.ts.map +1 -0
  21. package/dist/runtime/commitEffectResult.js +172 -0
  22. package/dist/runtime/constants.d.ts +2 -0
  23. package/dist/runtime/constants.d.ts.map +1 -0
  24. package/dist/runtime/constants.js +5 -0
  25. package/dist/runtime/createRun.d.ts +3 -0
  26. package/dist/runtime/createRun.d.ts.map +1 -0
  27. package/dist/runtime/createRun.js +81 -0
  28. package/dist/runtime/errorUtils.d.ts +10 -0
  29. package/dist/runtime/errorUtils.d.ts.map +1 -0
  30. package/dist/runtime/errorUtils.js +42 -0
  31. package/dist/runtime/exceptions.d.ts +45 -0
  32. package/dist/runtime/exceptions.d.ts.map +1 -0
  33. package/dist/runtime/exceptions.js +99 -0
  34. package/dist/runtime/index.d.ts +12 -0
  35. package/dist/runtime/index.d.ts.map +1 -0
  36. package/dist/runtime/index.js +34 -0
  37. package/dist/runtime/instrumentation.d.ts +6 -0
  38. package/dist/runtime/instrumentation.d.ts.map +1 -0
  39. package/dist/runtime/instrumentation.js +14 -0
  40. package/dist/runtime/intrinsics/breakpoint.d.ts +4 -0
  41. package/dist/runtime/intrinsics/breakpoint.d.ts.map +1 -0
  42. package/dist/runtime/intrinsics/breakpoint.js +42 -0
  43. package/dist/runtime/intrinsics/index.d.ts +7 -0
  44. package/dist/runtime/intrinsics/index.d.ts.map +1 -0
  45. package/dist/runtime/intrinsics/index.js +15 -0
  46. package/dist/runtime/intrinsics/orchestratorTask.d.ts +4 -0
  47. package/dist/runtime/intrinsics/orchestratorTask.d.ts.map +1 -0
  48. package/dist/runtime/intrinsics/orchestratorTask.js +28 -0
  49. package/dist/runtime/intrinsics/parallel.d.ts +5 -0
  50. package/dist/runtime/intrinsics/parallel.d.ts.map +1 -0
  51. package/dist/runtime/intrinsics/parallel.js +45 -0
  52. package/dist/runtime/intrinsics/sleep.d.ts +4 -0
  53. package/dist/runtime/intrinsics/sleep.d.ts.map +1 -0
  54. package/dist/runtime/intrinsics/sleep.js +70 -0
  55. package/dist/runtime/intrinsics/task.d.ts +20 -0
  56. package/dist/runtime/intrinsics/task.d.ts.map +1 -0
  57. package/dist/runtime/intrinsics/task.js +237 -0
  58. package/dist/runtime/invocation/hashInvocationKey.d.ts +12 -0
  59. package/dist/runtime/invocation/hashInvocationKey.d.ts.map +1 -0
  60. package/dist/runtime/invocation/hashInvocationKey.js +12 -0
  61. package/dist/runtime/invocation/index.d.ts +3 -0
  62. package/dist/runtime/invocation/index.d.ts.map +1 -0
  63. package/dist/runtime/invocation/index.js +5 -0
  64. package/dist/runtime/orchestrateIteration.d.ts +3 -0
  65. package/dist/runtime/orchestrateIteration.d.ts.map +1 -0
  66. package/dist/runtime/orchestrateIteration.js +195 -0
  67. package/dist/runtime/processContext.d.ts +19 -0
  68. package/dist/runtime/processContext.d.ts.map +1 -0
  69. package/dist/runtime/processContext.js +55 -0
  70. package/dist/runtime/replay/createReplayEngine.d.ts +31 -0
  71. package/dist/runtime/replay/createReplayEngine.d.ts.map +1 -0
  72. package/dist/runtime/replay/createReplayEngine.js +82 -0
  73. package/dist/runtime/replay/effectIndex.d.ts +34 -0
  74. package/dist/runtime/replay/effectIndex.d.ts.map +1 -0
  75. package/dist/runtime/replay/effectIndex.js +241 -0
  76. package/dist/runtime/replay/index.d.ts +7 -0
  77. package/dist/runtime/replay/index.d.ts.map +1 -0
  78. package/dist/runtime/replay/index.js +19 -0
  79. package/dist/runtime/replay/replayCursor.d.ts +7 -0
  80. package/dist/runtime/replay/replayCursor.d.ts.map +1 -0
  81. package/dist/runtime/replay/replayCursor.js +22 -0
  82. package/dist/runtime/replay/stateCache.d.ts +48 -0
  83. package/dist/runtime/replay/stateCache.d.ts.map +1 -0
  84. package/dist/runtime/replay/stateCache.js +211 -0
  85. package/dist/runtime/types.d.ts +147 -0
  86. package/dist/runtime/types.d.ts.map +1 -0
  87. package/dist/runtime/types.js +2 -0
  88. package/dist/storage/atomic.d.ts +2 -0
  89. package/dist/storage/atomic.d.ts.map +1 -0
  90. package/dist/storage/atomic.js +54 -0
  91. package/dist/storage/cleanup.d.ts +4 -0
  92. package/dist/storage/cleanup.d.ts.map +1 -0
  93. package/dist/storage/cleanup.js +96 -0
  94. package/dist/storage/clock.d.ts +6 -0
  95. package/dist/storage/clock.d.ts.map +1 -0
  96. package/dist/storage/clock.js +29 -0
  97. package/dist/storage/createRunDir.d.ts +6 -0
  98. package/dist/storage/createRunDir.d.ts.map +1 -0
  99. package/dist/storage/createRunDir.js +59 -0
  100. package/dist/storage/index.d.ts +9 -0
  101. package/dist/storage/index.d.ts.map +1 -0
  102. package/dist/storage/index.js +28 -0
  103. package/dist/storage/journal.d.ts +4 -0
  104. package/dist/storage/journal.d.ts.map +1 -0
  105. package/dist/storage/journal.js +103 -0
  106. package/dist/storage/lock.d.ts +5 -0
  107. package/dist/storage/lock.d.ts.map +1 -0
  108. package/dist/storage/lock.js +41 -0
  109. package/dist/storage/paths.d.ts +19 -0
  110. package/dist/storage/paths.d.ts.map +1 -0
  111. package/dist/storage/paths.js +46 -0
  112. package/dist/storage/runFiles.d.ts +5 -0
  113. package/dist/storage/runFiles.d.ts.map +1 -0
  114. package/dist/storage/runFiles.js +39 -0
  115. package/dist/storage/snapshotState.d.ts +10 -0
  116. package/dist/storage/snapshotState.d.ts.map +1 -0
  117. package/dist/storage/snapshotState.js +15 -0
  118. package/dist/storage/storeTaskArtifacts.d.ts +6 -0
  119. package/dist/storage/storeTaskArtifacts.d.ts.map +1 -0
  120. package/dist/storage/storeTaskArtifacts.js +58 -0
  121. package/dist/storage/tasks.d.ts +17 -0
  122. package/dist/storage/tasks.d.ts.map +1 -0
  123. package/dist/storage/tasks.js +82 -0
  124. package/dist/storage/types.d.ts +112 -0
  125. package/dist/storage/types.d.ts.map +1 -0
  126. package/dist/storage/types.js +2 -0
  127. package/dist/storage/ulids.d.ts +11 -0
  128. package/dist/storage/ulids.d.ts.map +1 -0
  129. package/dist/storage/ulids.js +25 -0
  130. package/dist/tasks/batching.d.ts +29 -0
  131. package/dist/tasks/batching.d.ts.map +1 -0
  132. package/dist/tasks/batching.js +66 -0
  133. package/dist/tasks/context.d.ts +11 -0
  134. package/dist/tasks/context.d.ts.map +1 -0
  135. package/dist/tasks/context.js +181 -0
  136. package/dist/tasks/defineTask.d.ts +9 -0
  137. package/dist/tasks/defineTask.d.ts.map +1 -0
  138. package/dist/tasks/defineTask.js +58 -0
  139. package/dist/tasks/index.d.ts +8 -0
  140. package/dist/tasks/index.d.ts.map +1 -0
  141. package/dist/tasks/index.js +23 -0
  142. package/dist/tasks/kinds/index.d.ts +7 -0
  143. package/dist/tasks/kinds/index.d.ts.map +1 -0
  144. package/dist/tasks/kinds/index.js +333 -0
  145. package/dist/tasks/registry.d.ts +53 -0
  146. package/dist/tasks/registry.d.ts.map +1 -0
  147. package/dist/tasks/registry.js +145 -0
  148. package/dist/tasks/serializer.d.ts +60 -0
  149. package/dist/tasks/serializer.d.ts.map +1 -0
  150. package/dist/tasks/serializer.js +193 -0
  151. package/dist/tasks/types.d.ts +148 -0
  152. package/dist/tasks/types.d.ts.map +1 -0
  153. package/dist/tasks/types.js +2 -0
  154. package/dist/test-fixtures/kinds/index.d.ts +56 -0
  155. package/dist/test-fixtures/kinds/index.d.ts.map +1 -0
  156. package/dist/test-fixtures/kinds/index.js +44 -0
  157. package/dist/testing/deterministic.d.ts +90 -0
  158. package/dist/testing/deterministic.d.ts.map +1 -0
  159. package/dist/testing/deterministic.js +449 -0
  160. package/dist/testing/index.d.ts +4 -0
  161. package/dist/testing/index.d.ts.map +1 -0
  162. package/dist/testing/index.js +23 -0
  163. package/dist/testing/runHarness.d.ts +64 -0
  164. package/dist/testing/runHarness.d.ts.map +1 -0
  165. package/dist/testing/runHarness.js +161 -0
  166. package/dist/testing/snapshots.d.ts +17 -0
  167. package/dist/testing/snapshots.d.ts.map +1 -0
  168. package/dist/testing/snapshots.js +24 -0
  169. package/package.json +35 -0
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.NodeTaskRunnerError = void 0;
7
+ exports.runNodeTask = runNodeTask;
8
+ exports.commitNodeResult = commitNodeResult;
9
+ const child_process_1 = require("child_process");
10
+ const fs_1 = require("fs");
11
+ const path_1 = __importDefault(require("path"));
12
+ const tasks_1 = require("../storage/tasks");
13
+ const commitEffectResult_1 = require("../runtime/commitEffectResult");
14
+ const env_1 = require("./env");
15
+ const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
16
+ class NodeTaskRunnerError extends Error {
17
+ code;
18
+ constructor(message, code) {
19
+ super(message);
20
+ this.code = code;
21
+ this.name = "NodeTaskRunnerError";
22
+ }
23
+ }
24
+ exports.NodeTaskRunnerError = NodeTaskRunnerError;
25
+ async function runNodeTask(options) {
26
+ const task = options.task ?? (await loadNodeTask(options.runDir, options.effectId));
27
+ validateNodeTask(task, options.effectId);
28
+ const hydration = options.hydration ??
29
+ (0, env_1.resolveNodeTaskEnv)(task, {
30
+ baseEnv: options.baseEnv ?? process.env,
31
+ overrides: options.envOverrides,
32
+ inheritProcessEnv: options.cleanEnv ? false : options.inheritProcessEnv,
33
+ });
34
+ const entryPath = resolveWorkspacePath(options.workspaceRoot ?? options.runDir, task.node?.entry, "node.entry", options.effectId);
35
+ const cwdPath = resolveWorkspacePath(options.workspaceRoot ?? options.runDir, task.node?.cwd, "node.cwd", options.effectId);
36
+ const args = task.node?.args ? [...task.node.args] : [];
37
+ const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : task.node?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
38
+ const commandDetails = {
39
+ binary: options.nodeBinaryPath ?? process.execPath,
40
+ args: [entryPath, ...args],
41
+ cwd: cwdPath ?? options.runDir,
42
+ };
43
+ const envForProcess = { ...hydration.env };
44
+ const ioPaths = applyIoEnv(envForProcess, options.runDir, options.effectId, task.io);
45
+ if (options.dryRun) {
46
+ const nowIso = new Date().toISOString();
47
+ return {
48
+ task,
49
+ stdout: "",
50
+ stderr: "",
51
+ exitCode: null,
52
+ signal: null,
53
+ timedOut: false,
54
+ startedAt: nowIso,
55
+ finishedAt: nowIso,
56
+ durationMs: 0,
57
+ timeoutMs,
58
+ command: commandDetails,
59
+ io: ioPaths,
60
+ hydrated: hydration,
61
+ };
62
+ }
63
+ await ensureParentDir(ioPaths.inputJsonPath);
64
+ await ensureParentDir(ioPaths.outputJsonPath);
65
+ await ensureParentDir(ioPaths.stdoutPath);
66
+ await ensureParentDir(ioPaths.stderrPath);
67
+ await stageTaskInputs({
68
+ task,
69
+ runDir: options.runDir,
70
+ effectId: options.effectId,
71
+ inputJsonPath: ioPaths.inputJsonPath,
72
+ });
73
+ const startedAtMs = Date.now();
74
+ const startedAtIso = new Date(startedAtMs).toISOString();
75
+ const runResult = await spawnNodeProcess({
76
+ command: commandDetails.binary,
77
+ args: commandDetails.args,
78
+ cwd: commandDetails.cwd,
79
+ env: envForProcess,
80
+ timeoutMs,
81
+ stdoutPath: ioPaths.stdoutPath,
82
+ stderrPath: ioPaths.stderrPath,
83
+ onStdoutChunk: options.onStdoutChunk,
84
+ onStderrChunk: options.onStderrChunk,
85
+ });
86
+ const finishedAtMs = Date.now();
87
+ const finishedAtIso = new Date(finishedAtMs).toISOString();
88
+ let parsedOutput;
89
+ parsedOutput = await readOptionalJson(ioPaths.outputJsonPath);
90
+ return {
91
+ task,
92
+ stdout: runResult.stdout,
93
+ stderr: runResult.stderr,
94
+ exitCode: runResult.exitCode,
95
+ signal: runResult.signal,
96
+ timedOut: runResult.timedOut,
97
+ startedAt: startedAtIso,
98
+ finishedAt: finishedAtIso,
99
+ durationMs: finishedAtMs - startedAtMs,
100
+ timeoutMs,
101
+ output: parsedOutput,
102
+ command: commandDetails,
103
+ io: ioPaths,
104
+ hydrated: hydration,
105
+ };
106
+ }
107
+ async function commitNodeResult(options) {
108
+ const { runDir, effectId, invocationKey, logger, result } = options;
109
+ const stdoutRef = toRunRelativePosix(runDir, result.io.stdoutPath);
110
+ const stderrRef = toRunRelativePosix(runDir, result.io.stderrPath);
111
+ const outputJsonRef = toRunRelativePosix(runDir, result.io.outputJsonPath);
112
+ const status = !result.timedOut && (result.exitCode === null || result.exitCode === 0) ? "ok" : "error";
113
+ const metadata = {
114
+ durationMs: result.durationMs,
115
+ timeoutMs: result.timeoutMs,
116
+ exitCode: result.exitCode,
117
+ signal: result.signal ?? undefined,
118
+ timedOut: result.timedOut,
119
+ outputJsonRef,
120
+ };
121
+ return (0, commitEffectResult_1.commitEffectResult)({
122
+ runDir,
123
+ effectId,
124
+ invocationKey,
125
+ logger,
126
+ result: {
127
+ status,
128
+ value: status === "ok" ? result.output : undefined,
129
+ error: status === "error" ? buildNodeTaskErrorPayload(result, outputJsonRef) : undefined,
130
+ stdout: result.stdout,
131
+ stderr: result.stderr,
132
+ stdoutRef,
133
+ stderrRef,
134
+ startedAt: result.startedAt,
135
+ finishedAt: result.finishedAt,
136
+ metadata,
137
+ },
138
+ });
139
+ }
140
+ async function loadNodeTask(runDir, effectId) {
141
+ const json = await (0, tasks_1.readTaskDefinition)(runDir, effectId);
142
+ if (!json) {
143
+ throw new NodeTaskRunnerError(`Task definition for effect ${effectId} is missing`, "missing_task");
144
+ }
145
+ return json;
146
+ }
147
+ function validateNodeTask(task, effectId) {
148
+ if (task.kind !== "node" || !task.node) {
149
+ throw new NodeTaskRunnerError(`Effect ${effectId} is not a node task`, "invalid_task_kind");
150
+ }
151
+ if (!task.node.entry || typeof task.node.entry !== "string") {
152
+ throw new NodeTaskRunnerError(`Effect ${effectId} node.entry is missing`, "missing_entry");
153
+ }
154
+ }
155
+ function resolveWorkspacePath(baseDir, value, field, effectId) {
156
+ if (!value) {
157
+ return undefined;
158
+ }
159
+ const normalized = value.replace(/\\/g, "/");
160
+ const isAbsolute = path_1.default.isAbsolute(normalized) || /^[A-Za-z]:\//.test(normalized);
161
+ const resolved = isAbsolute ? normalized : path_1.default.join(baseDir, ...normalized.split("/"));
162
+ if (!resolved) {
163
+ throw new NodeTaskRunnerError(`Effect ${effectId} has an invalid ${field}`, "invalid_path");
164
+ }
165
+ return path_1.default.normalize(resolved);
166
+ }
167
+ function resolveRunRelativePath(runDir, relative) {
168
+ if (!relative)
169
+ return undefined;
170
+ if (path_1.default.isAbsolute(relative) || /^[A-Za-z]:[\\/]/.test(relative)) {
171
+ return path_1.default.normalize(relative);
172
+ }
173
+ const normalized = relative.replace(/\\/g, "/");
174
+ return path_1.default.join(runDir, ...normalized.split("/"));
175
+ }
176
+ function applyIoEnv(env, runDir, effectId, io) {
177
+ const normalizedIo = withDefaultIoHints(effectId, io);
178
+ const inputJsonPath = resolveRunRelativePath(runDir, normalizedIo.inputJsonPath);
179
+ const outputJsonPath = resolveRunRelativePath(runDir, normalizedIo.outputJsonPath);
180
+ const stdoutPath = resolveRunRelativePath(runDir, normalizedIo.stdoutPath);
181
+ const stderrPath = resolveRunRelativePath(runDir, normalizedIo.stderrPath);
182
+ env.BABYSITTER_INPUT_JSON = inputJsonPath;
183
+ env.BABYSITTER_OUTPUT_JSON = outputJsonPath;
184
+ env.BABYSITTER_STDOUT_PATH = stdoutPath;
185
+ env.BABYSITTER_STDERR_PATH = stderrPath;
186
+ env.BABYSITTER_EFFECT_ID = effectId;
187
+ return { inputJsonPath, outputJsonPath, stdoutPath, stderrPath };
188
+ }
189
+ async function readOptionalJson(filePath) {
190
+ try {
191
+ const contents = await fs_1.promises.readFile(filePath, "utf8");
192
+ if (!contents.trim()) {
193
+ return undefined;
194
+ }
195
+ return JSON.parse(contents);
196
+ }
197
+ catch (error) {
198
+ const err = error;
199
+ if (err.code === "ENOENT") {
200
+ return undefined;
201
+ }
202
+ throw error;
203
+ }
204
+ }
205
+ function withDefaultIoHints(effectId, io) {
206
+ const defaulted = {
207
+ inputJsonPath: `tasks/${effectId}/inputs.json`,
208
+ outputJsonPath: `tasks/${effectId}/result.json`,
209
+ stdoutPath: `tasks/${effectId}/stdout.log`,
210
+ stderrPath: `tasks/${effectId}/stderr.log`,
211
+ };
212
+ return {
213
+ inputJsonPath: typeof io?.inputJsonPath === "string" && io.inputJsonPath.trim().length > 0
214
+ ? io.inputJsonPath
215
+ : defaulted.inputJsonPath,
216
+ outputJsonPath: typeof io?.outputJsonPath === "string" && io.outputJsonPath.trim().length > 0
217
+ ? io.outputJsonPath
218
+ : defaulted.outputJsonPath,
219
+ stdoutPath: typeof io?.stdoutPath === "string" && io.stdoutPath.trim().length > 0 ? io.stdoutPath : defaulted.stdoutPath,
220
+ stderrPath: typeof io?.stderrPath === "string" && io.stderrPath.trim().length > 0 ? io.stderrPath : defaulted.stderrPath,
221
+ };
222
+ }
223
+ async function spawnNodeProcess(options) {
224
+ return new Promise((resolve, reject) => {
225
+ const child = (0, child_process_1.spawn)(options.command, options.args, {
226
+ cwd: options.cwd,
227
+ env: options.env,
228
+ stdio: ["ignore", "pipe", "pipe"],
229
+ });
230
+ const stdoutStream = (0, fs_1.createWriteStream)(options.stdoutPath, { flags: "w" });
231
+ const stderrStream = (0, fs_1.createWriteStream)(options.stderrPath, { flags: "w" });
232
+ let streamsClosed = false;
233
+ const finishStreams = () => {
234
+ if (streamsClosed)
235
+ return;
236
+ streamsClosed = true;
237
+ stdoutStream.end();
238
+ stderrStream.end();
239
+ };
240
+ let settled = false;
241
+ const finishWithError = (error) => {
242
+ if (settled)
243
+ return;
244
+ settled = true;
245
+ if (timeoutHandle)
246
+ clearTimeout(timeoutHandle);
247
+ finishStreams();
248
+ reject(error);
249
+ };
250
+ const finishWithSuccess = (payload) => {
251
+ if (settled)
252
+ return;
253
+ settled = true;
254
+ if (timeoutHandle)
255
+ clearTimeout(timeoutHandle);
256
+ finishStreams();
257
+ resolve(payload);
258
+ };
259
+ let stdout = "";
260
+ let stderr = "";
261
+ let timedOut = false;
262
+ child.stdout?.setEncoding("utf8");
263
+ child.stdout?.on("data", (chunk) => {
264
+ stdout += chunk;
265
+ stdoutStream.write(chunk);
266
+ options.onStdoutChunk?.(chunk);
267
+ });
268
+ child.stderr?.setEncoding("utf8");
269
+ child.stderr?.on("data", (chunk) => {
270
+ stderr += chunk;
271
+ stderrStream.write(chunk);
272
+ options.onStderrChunk?.(chunk);
273
+ });
274
+ stdoutStream.on("error", (error) => {
275
+ child.kill();
276
+ finishWithError(new NodeTaskRunnerError(`Failed to write stdout log: ${error.message}`, "stdout_log_error"));
277
+ });
278
+ stderrStream.on("error", (error) => {
279
+ child.kill();
280
+ finishWithError(new NodeTaskRunnerError(`Failed to write stderr log: ${error.message}`, "stderr_log_error"));
281
+ });
282
+ const timeoutHandle = options.timeoutMs > 0
283
+ ? setTimeout(() => {
284
+ timedOut = true;
285
+ child.kill();
286
+ }, options.timeoutMs)
287
+ : undefined;
288
+ child.on("error", (error) => {
289
+ finishWithError(new NodeTaskRunnerError(`Failed to spawn node task: ${error.message}`, "spawn_error"));
290
+ });
291
+ child.on("close", (code, signal) => {
292
+ finishWithSuccess({ stdout, stderr, exitCode: code, signal, timedOut });
293
+ });
294
+ });
295
+ }
296
+ async function ensureParentDir(filePath) {
297
+ await fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
298
+ }
299
+ async function stageTaskInputs(options) {
300
+ const inlineInputs = options.task.inputs;
301
+ const hasInlineInputs = inlineInputs !== undefined;
302
+ const maybeInputsRef = options.task.inputsRef;
303
+ const inputsRef = typeof maybeInputsRef === "string" ? maybeInputsRef : undefined;
304
+ if (!hasInlineInputs && !inputsRef) {
305
+ return;
306
+ }
307
+ const payload = hasInlineInputs && inlineInputs !== undefined
308
+ ? inlineInputs
309
+ : await readInputsRefValue(options.runDir, inputsRef, options.effectId);
310
+ await fs_1.promises.writeFile(options.inputJsonPath, JSON.stringify(payload, null, 2) + "\n", "utf8");
311
+ }
312
+ async function readInputsRefValue(runDir, ref, effectId) {
313
+ const resolved = resolveRunRelativePath(runDir, ref);
314
+ if (!resolved) {
315
+ throw new NodeTaskRunnerError(`Effect ${effectId} has an invalid inputsRef path`, "invalid_inputs_ref");
316
+ }
317
+ try {
318
+ const contents = await fs_1.promises.readFile(resolved, "utf8");
319
+ if (!contents.trim()) {
320
+ throw new NodeTaskRunnerError(`Effect ${effectId} inputsRef file is empty`, "empty_inputs_ref");
321
+ }
322
+ return JSON.parse(contents);
323
+ }
324
+ catch (error) {
325
+ const err = error;
326
+ if (err.code === "ENOENT") {
327
+ throw new NodeTaskRunnerError(`Effect ${effectId} inputsRef file is missing`, "missing_inputs_ref");
328
+ }
329
+ throw error;
330
+ }
331
+ }
332
+ function buildNodeTaskErrorPayload(result, outputJsonRef) {
333
+ const base = result.exitCode === null
334
+ ? "Node task exited without a code"
335
+ : `Node task exited with code ${result.exitCode}${result.signal ? ` (signal ${result.signal})` : ""}`;
336
+ const message = result.timedOut ? `Node task timed out after ${result.timeoutMs}ms` : base;
337
+ return {
338
+ message,
339
+ exitCode: result.exitCode,
340
+ signal: result.signal ?? undefined,
341
+ timedOut: result.timedOut,
342
+ stdout: result.stdout,
343
+ stderr: result.stderr,
344
+ outputJsonRef,
345
+ };
346
+ }
347
+ function toRunRelativePosix(runDir, absolutePath) {
348
+ if (!absolutePath)
349
+ return undefined;
350
+ const relative = path_1.default.relative(runDir, absolutePath);
351
+ if (!relative || relative.startsWith(".."))
352
+ return undefined;
353
+ return relative.replace(/\\/g, "/");
354
+ }
@@ -0,0 +1,3 @@
1
+ import { CommitEffectResultArtifacts, CommitEffectResultOptions } from "./types";
2
+ export declare function commitEffectResult(options: CommitEffectResultOptions): Promise<CommitEffectResultArtifacts>;
3
+ //# sourceMappingURL=commitEffectResult.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitEffectResult.d.ts","sourceRoot":"","sources":["../../src/runtime/commitEffectResult.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EAG1B,MAAM,SAAS,CAAC;AAOjB,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAoEjH"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.commitEffectResult = commitEffectResult;
4
+ const journal_1 = require("../storage/journal");
5
+ const effectIndex_1 = require("./replay/effectIndex");
6
+ const exceptions_1 = require("./exceptions");
7
+ const errorUtils_1 = require("./errorUtils");
8
+ const instrumentation_1 = require("./instrumentation");
9
+ const registry_1 = require("../tasks/registry");
10
+ const serializer_1 = require("../tasks/serializer");
11
+ async function commitEffectResult(options) {
12
+ guardResultPayload(options);
13
+ const effectIndex = await (0, effectIndex_1.buildEffectIndex)({ runDir: options.runDir });
14
+ const record = effectIndex.getByEffectId(options.effectId);
15
+ if (!record) {
16
+ logCommitFailure(options, "unknown_effect");
17
+ throw new exceptions_1.RunFailedError(`Unknown effectId ${options.effectId}`);
18
+ }
19
+ if (record.status !== "requested") {
20
+ logCommitFailure(options, "already_resolved", { currentStatus: record.status });
21
+ throw new exceptions_1.RunFailedError(`Effect ${options.effectId} is already resolved`);
22
+ }
23
+ ensureInvocationKeyMatches(options, record);
24
+ const resultPayload = buildResultPayload(options);
25
+ const { resultRef, stdoutRef: writtenStdoutRef, stderrRef: writtenStderrRef } = await (0, serializer_1.serializeAndWriteTaskResult)({
26
+ runDir: options.runDir,
27
+ effectId: options.effectId,
28
+ taskId: requireTaskId(record),
29
+ invocationKey: record.invocationKey,
30
+ payload: resultPayload,
31
+ });
32
+ const stdoutRef = resultPayload.stdoutRef ?? writtenStdoutRef;
33
+ const stderrRef = resultPayload.stderrRef ?? writtenStderrRef;
34
+ const eventError = resultPayload.status === "error" ? resultPayload.error : undefined;
35
+ const resolvedEvent = await (0, journal_1.appendEvent)({
36
+ runDir: options.runDir,
37
+ eventType: "EFFECT_RESOLVED",
38
+ event: {
39
+ effectId: options.effectId,
40
+ status: options.result.status,
41
+ resultRef,
42
+ error: eventError,
43
+ stdoutRef,
44
+ stderrRef,
45
+ startedAt: resultPayload.startedAt,
46
+ finishedAt: resultPayload.finishedAt,
47
+ },
48
+ });
49
+ registry_1.globalTaskRegistry.resolveEffect(options.effectId, {
50
+ status: options.result.status === "ok" ? "resolved_ok" : "resolved_error",
51
+ resultRef,
52
+ stdoutRef,
53
+ stderrRef,
54
+ resolvedAt: resolvedEvent.recordedAt,
55
+ });
56
+ (0, instrumentation_1.emitRuntimeMetric)(options.logger, "commit.effect", {
57
+ effectId: options.effectId,
58
+ invocationKey: record.invocationKey,
59
+ status: options.result.status,
60
+ runDir: options.runDir,
61
+ hasStdout: Boolean(stdoutRef),
62
+ hasStderr: Boolean(stderrRef),
63
+ });
64
+ return {
65
+ resultRef,
66
+ stdoutRef: stdoutRef ?? undefined,
67
+ stderrRef: stderrRef ?? undefined,
68
+ startedAt: resultPayload.startedAt,
69
+ finishedAt: resultPayload.finishedAt,
70
+ };
71
+ }
72
+ function ensureInvocationKeyMatches(options, record) {
73
+ if (!options.invocationKey)
74
+ return;
75
+ if (options.invocationKey === record.invocationKey) {
76
+ return;
77
+ }
78
+ logCommitFailure(options, "invocation_mismatch", {
79
+ expectedInvocationKey: record.invocationKey,
80
+ providedInvocationKey: options.invocationKey,
81
+ });
82
+ throw new exceptions_1.RunFailedError(`Invocation key mismatch for effect ${options.effectId}`, {
83
+ effectId: options.effectId,
84
+ expectedInvocationKey: record.invocationKey,
85
+ providedInvocationKey: options.invocationKey,
86
+ });
87
+ }
88
+ function serializeEffectError(error) {
89
+ return (0, errorUtils_1.toSerializedEffectError)(error);
90
+ }
91
+ function guardResultPayload(options) {
92
+ try {
93
+ validateResultPayload(options);
94
+ }
95
+ catch (error) {
96
+ logCommitFailure(options, "invalid_payload", {
97
+ message: error instanceof Error ? error.message : String(error),
98
+ });
99
+ throw error;
100
+ }
101
+ }
102
+ function validateResultPayload(options) {
103
+ if (options.result.status === "ok" && options.result.error !== undefined) {
104
+ throw new exceptions_1.RunFailedError("Cannot provide an error payload when result status is 'ok'");
105
+ }
106
+ if (options.result.status === "error" && options.result.error === undefined) {
107
+ throw new exceptions_1.RunFailedError("Missing error payload for result status 'error'");
108
+ }
109
+ if (options.result.stdout !== undefined && typeof options.result.stdout !== "string") {
110
+ throw new exceptions_1.RunFailedError("stdout must be a string when provided");
111
+ }
112
+ if (options.result.stderr !== undefined && typeof options.result.stderr !== "string") {
113
+ throw new exceptions_1.RunFailedError("stderr must be a string when provided");
114
+ }
115
+ if (options.result.stdoutRef !== undefined && typeof options.result.stdoutRef !== "string") {
116
+ throw new exceptions_1.RunFailedError("stdoutRef must be a string when provided");
117
+ }
118
+ if (options.result.stderrRef !== undefined && typeof options.result.stderrRef !== "string") {
119
+ throw new exceptions_1.RunFailedError("stderrRef must be a string when provided");
120
+ }
121
+ if (options.result.startedAt !== undefined && typeof options.result.startedAt !== "string") {
122
+ throw new exceptions_1.RunFailedError("startedAt must be an ISO timestamp string when provided");
123
+ }
124
+ if (options.result.finishedAt !== undefined && typeof options.result.finishedAt !== "string") {
125
+ throw new exceptions_1.RunFailedError("finishedAt must be an ISO timestamp string when provided");
126
+ }
127
+ if (options.result.metadata !== undefined &&
128
+ (options.result.metadata === null || typeof options.result.metadata !== "object" || Array.isArray(options.result.metadata))) {
129
+ throw new exceptions_1.RunFailedError("metadata must be a JsonRecord when provided");
130
+ }
131
+ }
132
+ function logCommitFailure(options, reason, extra = {}) {
133
+ (0, instrumentation_1.emitRuntimeMetric)(options.logger, "commit.effect", {
134
+ effectId: options.effectId,
135
+ status: "rejected",
136
+ reason,
137
+ runDir: options.runDir,
138
+ invocationKey: options.invocationKey,
139
+ ...extra,
140
+ });
141
+ }
142
+ function buildResultPayload(options) {
143
+ const base = {
144
+ stdout: options.result.stdout,
145
+ stderr: options.result.stderr,
146
+ stdoutRef: options.result.stdoutRef,
147
+ stderrRef: options.result.stderrRef,
148
+ startedAt: options.result.startedAt,
149
+ finishedAt: options.result.finishedAt,
150
+ metadata: options.result.metadata,
151
+ };
152
+ if (options.result.status === "ok") {
153
+ return {
154
+ status: "ok",
155
+ result: options.result.value,
156
+ ...base,
157
+ };
158
+ }
159
+ return {
160
+ status: "error",
161
+ error: serializeEffectError(options.result.error),
162
+ ...base,
163
+ };
164
+ }
165
+ function requireTaskId(record) {
166
+ if (record.taskId) {
167
+ return record.taskId;
168
+ }
169
+ throw new exceptions_1.RunFailedError(`Effect ${record.effectId} is missing task id metadata`, {
170
+ effectId: record.effectId,
171
+ });
172
+ }
@@ -0,0 +1,2 @@
1
+ export declare const replaySchemaVersion = "2026.01-storage-preview";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/runtime/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,4BAAyB,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaySchemaVersion = void 0;
4
+ const paths_1 = require("../storage/paths");
5
+ exports.replaySchemaVersion = paths_1.DEFAULT_LAYOUT_VERSION;
@@ -0,0 +1,3 @@
1
+ import type { CreateRunOptions, CreateRunResult } from "./types";
2
+ export declare function createRun(options: CreateRunOptions): Promise<CreateRunResult>;
3
+ //# sourceMappingURL=createRun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRun.d.ts","sourceRoot":"","sources":["../../src/runtime/createRun.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEjE,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAkDnF"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createRun = createRun;
7
+ const path_1 = __importDefault(require("path"));
8
+ const createRunDir_1 = require("../storage/createRunDir");
9
+ const journal_1 = require("../storage/journal");
10
+ const lock_1 = require("../storage/lock");
11
+ const paths_1 = require("../storage/paths");
12
+ const ulids_1 = require("../storage/ulids");
13
+ async function createRun(options) {
14
+ const runId = options.runId ?? (0, ulids_1.nextUlid)();
15
+ validateRunId(runId);
16
+ const runDir = (0, paths_1.getRunDir)(options.runsDir, runId);
17
+ const normalizedEntrypoint = normalizeEntrypoint(runDir, options.process.importPath, options.process.exportName);
18
+ const requestId = options.request ?? options.process.processId ?? runId;
19
+ const { metadata } = await (0, createRunDir_1.createRunDir)({
20
+ runsRoot: options.runsDir,
21
+ runId,
22
+ request: requestId,
23
+ processId: options.process.processId,
24
+ processRevision: options.processRevision,
25
+ layoutVersion: options.layoutVersion,
26
+ inputs: options.inputs,
27
+ entrypoint: normalizedEntrypoint,
28
+ processPath: normalizedEntrypoint.importPath,
29
+ extraMetadata: options.metadata,
30
+ });
31
+ let lockAcquired = false;
32
+ try {
33
+ await (0, lock_1.acquireRunLock)(runDir, options.lockOwner ?? "runtime:createRun");
34
+ lockAcquired = true;
35
+ const eventPayload = {
36
+ runId,
37
+ processId: metadata.processId,
38
+ entrypoint: metadata.entrypoint,
39
+ };
40
+ if (metadata.processRevision) {
41
+ eventPayload.processRevision = metadata.processRevision;
42
+ }
43
+ if (options.inputs !== undefined) {
44
+ eventPayload.inputsRef = paths_1.INPUTS_FILE;
45
+ }
46
+ await (0, journal_1.appendEvent)({
47
+ runDir,
48
+ eventType: "RUN_CREATED",
49
+ event: eventPayload,
50
+ });
51
+ }
52
+ finally {
53
+ if (lockAcquired) {
54
+ await (0, lock_1.releaseRunLock)(runDir);
55
+ }
56
+ }
57
+ return {
58
+ runId,
59
+ runDir,
60
+ metadata,
61
+ };
62
+ }
63
+ function validateRunId(runId) {
64
+ if (typeof runId !== "string" || runId.trim() === "") {
65
+ throw new Error("runId must be a non-empty string");
66
+ }
67
+ }
68
+ function normalizeEntrypoint(runDir, importPath, exportName) {
69
+ const entryImport = toRunRelativePosix(runDir, importPath);
70
+ return {
71
+ importPath: entryImport,
72
+ exportName: exportName ?? "process",
73
+ };
74
+ }
75
+ function toRunRelativePosix(runDir, importPath) {
76
+ const relative = path_1.default.isAbsolute(importPath) ? path_1.default.relative(runDir, importPath) : importPath;
77
+ if (!relative || relative === ".") {
78
+ throw new Error("Entrypoint import path must reference a file");
79
+ }
80
+ return path_1.default.posix.normalize(relative.replace(/\\/g, "/"));
81
+ }
@@ -0,0 +1,10 @@
1
+ import { SerializedEffectError } from "./types";
2
+ export interface SerializedRuntimeError {
3
+ name: string;
4
+ message: string;
5
+ stack?: string;
6
+ data?: unknown;
7
+ }
8
+ export declare function serializeUnknownError(error: unknown): SerializedRuntimeError;
9
+ export declare function toSerializedEffectError(error: unknown): SerializedEffectError;
10
+ //# sourceMappingURL=errorUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorUtils.d.ts","sourceRoot":"","sources":["../../src/runtime/errorUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAqB5E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAkB7E"}