@mastra/docker 0.9.0 → 0.9.1-alpha.1
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.cjs +131 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +131 -39
- package/dist/index.js.map +1 -1
- package/dist/sandbox/process-manager.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -166,6 +166,61 @@ case "$err" in
|
|
|
166
166
|
esac
|
|
167
167
|
`;
|
|
168
168
|
/**
|
|
169
|
+
* Upper bound on how long an early stream 'end' waits for an in-flight kill
|
|
170
|
+
* confirmation before settling without termination metadata. The kill helper's
|
|
171
|
+
* own work is bounded (the KILL_SCRIPT polls for at most ~4s), so this only
|
|
172
|
+
* absorbs daemon round-trips; it exists so a wedged `inspect()` cannot leave
|
|
173
|
+
* wait() pending forever.
|
|
174
|
+
*/
|
|
175
|
+
const TERMINATION_CONFIRMATION_DEADLINE_MS = 1e4;
|
|
176
|
+
/**
|
|
177
|
+
* Resolves when `termination` settles, or after
|
|
178
|
+
* TERMINATION_CONFIRMATION_DEADLINE_MS — whichever comes first. Never rejects.
|
|
179
|
+
*/
|
|
180
|
+
function waitForTermination(termination) {
|
|
181
|
+
return new Promise((resolve) => {
|
|
182
|
+
const timer = setTimeout(resolve, TERMINATION_CONFIRMATION_DEADLINE_MS);
|
|
183
|
+
const done = () => {
|
|
184
|
+
clearTimeout(timer);
|
|
185
|
+
resolve();
|
|
186
|
+
};
|
|
187
|
+
termination.then(done, done);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Resolves with `work`'s value, or `fallback` if it has not settled within `ms`.
|
|
192
|
+
* Never rejects. Bounds a single daemon round-trip, so one call that never
|
|
193
|
+
* answers cannot leave wait() pending past its deadline.
|
|
194
|
+
*/
|
|
195
|
+
function withDeadline(work, ms, fallback) {
|
|
196
|
+
return new Promise((resolve) => {
|
|
197
|
+
const timer = setTimeout(() => resolve(fallback), Math.max(0, ms));
|
|
198
|
+
const done = (value) => {
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
resolve(value);
|
|
201
|
+
};
|
|
202
|
+
work.then(done, () => done(fallback));
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Polls `exec.inspect()` until the exec reports it is no longer running, bounded
|
|
207
|
+
* by `deadline` (an absolute `Date.now()` timestamp). A stream can close without
|
|
208
|
+
* 'end' while the process is still alive, and settling from inspect then would
|
|
209
|
+
* publish an exit for a process that never exited — but waiting unbounded could
|
|
210
|
+
* leave wait() pending forever, so each call is bounded too. Returns the last
|
|
211
|
+
* inspect result, which may still report Running: true if the bound was reached,
|
|
212
|
+
* or undefined if no call settled in time.
|
|
213
|
+
*/
|
|
214
|
+
async function waitForExecToStop(exec, deadline) {
|
|
215
|
+
let info;
|
|
216
|
+
while (Date.now() < deadline) {
|
|
217
|
+
info = await withDeadline(exec.inspect(), deadline - Date.now(), void 0);
|
|
218
|
+
if (!info || !info.Running) return info;
|
|
219
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
220
|
+
}
|
|
221
|
+
return info;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
169
224
|
* Wraps a Docker exec instance to conform to Mastra's ProcessHandle.
|
|
170
225
|
* Not exported — internal to this module.
|
|
171
226
|
*
|
|
@@ -182,6 +237,12 @@ var DockerProcessHandle = class extends _mastra_core_workspace.ProcessHandle {
|
|
|
182
237
|
_killed = false;
|
|
183
238
|
/** @internal Set by the timeout path to distinguish timeout kills from explicit kills */
|
|
184
239
|
_timedOut = false;
|
|
240
|
+
/**
|
|
241
|
+
* @internal In-flight kill confirmation, set while kill() is verifying the
|
|
242
|
+
* process group is gone. An early stream 'end' awaits it (bounded) so
|
|
243
|
+
* termination metadata is not dropped when the target's stream closes first.
|
|
244
|
+
*/
|
|
245
|
+
_terminationPromise = null;
|
|
185
246
|
_waitPromise = null;
|
|
186
247
|
_stdinStream = null;
|
|
187
248
|
_execStream = null;
|
|
@@ -224,6 +285,20 @@ var DockerProcessHandle = class extends _mastra_core_workspace.ProcessHandle {
|
|
|
224
285
|
}
|
|
225
286
|
async kill() {
|
|
226
287
|
if (this._exitCode !== void 0) return false;
|
|
288
|
+
if (this._terminationPromise) return this._terminationPromise;
|
|
289
|
+
const termination = this._runKillHelper();
|
|
290
|
+
this._terminationPromise = termination;
|
|
291
|
+
try {
|
|
292
|
+
return await termination;
|
|
293
|
+
} finally {
|
|
294
|
+
if (this._terminationPromise === termination) this._terminationPromise = null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Runs the kill helper and verifies the process group is gone before reporting
|
|
299
|
+
* success, so a `true` result means the targets have actually stopped.
|
|
300
|
+
*/
|
|
301
|
+
async _runKillHelper() {
|
|
227
302
|
try {
|
|
228
303
|
const killExec = await this._container.exec({
|
|
229
304
|
Cmd: [
|
|
@@ -350,52 +425,68 @@ var DockerProcessManager = class extends _mastra_core_workspace.SandboxProcessMa
|
|
|
350
425
|
}
|
|
351
426
|
if (combined.length > 0 && buffer.length === 0) buffer.push(combined);
|
|
352
427
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
success: exitCode === 0,
|
|
359
|
-
exitCode,
|
|
360
|
-
stdout: handle.stdout,
|
|
361
|
-
stderr: handle.stderr,
|
|
362
|
-
executionTimeMs: Date.now() - startTime
|
|
363
|
-
});
|
|
364
|
-
} catch {
|
|
365
|
-
handle._setExitCode(1);
|
|
366
|
-
resolve({
|
|
367
|
-
success: false,
|
|
368
|
-
exitCode: 1,
|
|
369
|
-
stdout: handle.stdout,
|
|
370
|
-
stderr: handle.stderr,
|
|
371
|
-
executionTimeMs: Date.now() - startTime
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
stream.on("close", () => {
|
|
376
|
-
if (handle.exitCode !== void 0) return;
|
|
377
|
-
if (!handle._killed) return;
|
|
378
|
-
handle._setExitCode(137);
|
|
428
|
+
let settled = false;
|
|
429
|
+
const settle = (exitCode, metadata = {}) => {
|
|
430
|
+
if (settled) return;
|
|
431
|
+
settled = true;
|
|
432
|
+
handle._setExitCode(exitCode);
|
|
379
433
|
resolve({
|
|
380
|
-
success:
|
|
381
|
-
exitCode
|
|
434
|
+
success: exitCode === 0,
|
|
435
|
+
exitCode,
|
|
382
436
|
stdout: handle.stdout,
|
|
383
437
|
stderr: handle.stderr,
|
|
384
438
|
executionTimeMs: Date.now() - startTime,
|
|
439
|
+
...metadata
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
const settleTerminated = () => {
|
|
443
|
+
settle(137, {
|
|
385
444
|
killed: true,
|
|
386
445
|
timedOut: handle._timedOut
|
|
387
446
|
});
|
|
447
|
+
};
|
|
448
|
+
stream.on("end", async () => {
|
|
449
|
+
if (handle._terminationPromise) await waitForTermination(handle._terminationPromise);
|
|
450
|
+
if (settled) return;
|
|
451
|
+
if (handle._killed) {
|
|
452
|
+
settleTerminated();
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const info = await exec.inspect();
|
|
457
|
+
settle(info.ExitCode ?? 1);
|
|
458
|
+
} catch {
|
|
459
|
+
settle(1);
|
|
460
|
+
}
|
|
388
461
|
});
|
|
389
|
-
stream.on("
|
|
390
|
-
|
|
391
|
-
handle.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
462
|
+
stream.on("close", async () => {
|
|
463
|
+
const deadline = Date.now() + TERMINATION_CONFIRMATION_DEADLINE_MS;
|
|
464
|
+
if (handle._terminationPromise) await waitForTermination(handle._terminationPromise);
|
|
465
|
+
if (settled) return;
|
|
466
|
+
if (handle._killed) {
|
|
467
|
+
settleTerminated();
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
const info = await waitForExecToStop(exec, deadline);
|
|
472
|
+
if (settled) return;
|
|
473
|
+
if (handle._killed) {
|
|
474
|
+
settleTerminated();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
settle(info?.ExitCode ?? 1);
|
|
478
|
+
} catch {
|
|
479
|
+
settle(1);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
stream.on("error", async () => {
|
|
483
|
+
if (handle._terminationPromise) await waitForTermination(handle._terminationPromise);
|
|
484
|
+
if (settled) return;
|
|
485
|
+
if (handle._killed) {
|
|
486
|
+
settleTerminated();
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
settle(1, { stderr: handle.stderr || "Stream error" });
|
|
399
490
|
});
|
|
400
491
|
});
|
|
401
492
|
const resolvedTimeout = options.timeout ?? this._defaultTimeout;
|
|
@@ -1840,7 +1931,8 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1840
1931
|
} else {
|
|
1841
1932
|
const stream = await waitForAbortable(docker.buildImage(context, {
|
|
1842
1933
|
t: tag,
|
|
1843
|
-
nocache
|
|
1934
|
+
nocache,
|
|
1935
|
+
forcerm: true
|
|
1844
1936
|
}).then((stream) => {
|
|
1845
1937
|
if (abortSignal?.aborted) {
|
|
1846
1938
|
stream.destroy?.();
|