@agent-relay/sandbox 0.1.2 → 0.1.3
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/README.md +226 -5
- package/dist/agent37/client.d.ts +115 -0
- package/dist/agent37/client.d.ts.map +1 -0
- package/dist/agent37/client.js +270 -0
- package/dist/agent37/client.js.map +1 -0
- package/dist/agent37/runtime.d.ts +415 -0
- package/dist/agent37/runtime.d.ts.map +1 -0
- package/dist/agent37/runtime.js +855 -0
- package/dist/agent37/runtime.js.map +1 -0
- package/dist/agentcore/bench.d.ts +93 -0
- package/dist/agentcore/bench.d.ts.map +1 -0
- package/dist/agentcore/bench.js +125 -0
- package/dist/agentcore/bench.js.map +1 -0
- package/dist/agentcore/capabilities.d.ts +169 -0
- package/dist/agentcore/capabilities.d.ts.map +1 -0
- package/dist/agentcore/capabilities.js +153 -0
- package/dist/agentcore/capabilities.js.map +1 -0
- package/dist/agentcore/config.d.ts +156 -0
- package/dist/agentcore/config.d.ts.map +1 -0
- package/dist/agentcore/config.js +12 -0
- package/dist/agentcore/config.js.map +1 -0
- package/dist/agentcore/internal/sdk.d.ts +181 -0
- package/dist/agentcore/internal/sdk.d.ts.map +1 -0
- package/dist/agentcore/internal/sdk.js +223 -0
- package/dist/agentcore/internal/sdk.js.map +1 -0
- package/dist/agentcore/runtime.d.ts +177 -0
- package/dist/agentcore/runtime.d.ts.map +1 -0
- package/dist/agentcore/runtime.js +706 -0
- package/dist/agentcore/runtime.js.map +1 -0
- package/dist/daytona/runtime.d.ts +23 -0
- package/dist/daytona/runtime.d.ts.map +1 -1
- package/dist/daytona/runtime.js +167 -5
- package/dist/daytona/runtime.js.map +1 -1
- package/dist/daytona/wire-supplement.d.ts +25 -0
- package/dist/daytona/wire-supplement.d.ts.map +1 -0
- package/dist/daytona/wire-supplement.js +16 -0
- package/dist/daytona/wire-supplement.js.map +1 -0
- package/dist/e2b/runtime.d.ts +107 -72
- package/dist/e2b/runtime.d.ts.map +1 -1
- package/dist/e2b/runtime.js +675 -190
- package/dist/e2b/runtime.js.map +1 -1
- package/dist/freestyle/capabilities.d.ts +28 -0
- package/dist/freestyle/capabilities.d.ts.map +1 -0
- package/dist/freestyle/capabilities.js +42 -0
- package/dist/freestyle/capabilities.js.map +1 -0
- package/dist/freestyle/config.d.ts +36 -0
- package/dist/freestyle/config.d.ts.map +1 -0
- package/dist/freestyle/config.js +2 -0
- package/dist/freestyle/config.js.map +1 -0
- package/dist/freestyle/internal/sdk.d.ts +69 -0
- package/dist/freestyle/internal/sdk.d.ts.map +1 -0
- package/dist/freestyle/internal/sdk.js +19 -0
- package/dist/freestyle/internal/sdk.js.map +1 -0
- package/dist/freestyle/runtime.d.ts +163 -0
- package/dist/freestyle/runtime.d.ts.map +1 -0
- package/dist/freestyle/runtime.js +798 -0
- package/dist/freestyle/runtime.js.map +1 -0
- package/dist/index.d.ts +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/microsandbox/runtime.d.ts +793 -0
- package/dist/microsandbox/runtime.d.ts.map +1 -0
- package/dist/microsandbox/runtime.js +2303 -0
- package/dist/microsandbox/runtime.js.map +1 -0
- package/dist/modal/bench.d.ts +199 -0
- package/dist/modal/bench.d.ts.map +1 -0
- package/dist/modal/bench.js +450 -0
- package/dist/modal/bench.js.map +1 -0
- package/dist/modal/capabilities.d.ts +180 -0
- package/dist/modal/capabilities.d.ts.map +1 -0
- package/dist/modal/capabilities.js +159 -0
- package/dist/modal/capabilities.js.map +1 -0
- package/dist/modal/config.d.ts +192 -0
- package/dist/modal/config.d.ts.map +1 -0
- package/dist/modal/config.js +169 -0
- package/dist/modal/config.js.map +1 -0
- package/dist/modal/internal/sdk.d.ts +141 -0
- package/dist/modal/internal/sdk.d.ts.map +1 -0
- package/dist/modal/internal/sdk.js +51 -0
- package/dist/modal/internal/sdk.js.map +1 -0
- package/dist/modal/runtime.d.ts +271 -0
- package/dist/modal/runtime.d.ts.map +1 -0
- package/dist/modal/runtime.js +871 -0
- package/dist/modal/runtime.js.map +1 -0
- package/dist/mount-script.d.ts +16 -1
- package/dist/mount-script.d.ts.map +1 -1
- package/dist/mount-script.js +76 -15
- package/dist/mount-script.js.map +1 -1
- package/dist/orchestrator.d.ts +15 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +13 -1
- package/dist/orchestrator.js.map +1 -1
- package/dist/port.d.ts +144 -6
- package/dist/port.d.ts.map +1 -1
- package/dist/port.js +27 -5
- package/dist/port.js.map +1 -1
- package/dist/types.d.ts +50 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vercel/bench.d.ts +169 -0
- package/dist/vercel/bench.d.ts.map +1 -0
- package/dist/vercel/bench.js +337 -0
- package/dist/vercel/bench.js.map +1 -0
- package/dist/vercel/capabilities.d.ts +106 -0
- package/dist/vercel/capabilities.d.ts.map +1 -0
- package/dist/vercel/capabilities.js +128 -0
- package/dist/vercel/capabilities.js.map +1 -0
- package/dist/vercel/config.d.ts +119 -0
- package/dist/vercel/config.d.ts.map +1 -0
- package/dist/vercel/config.js +8 -0
- package/dist/vercel/config.js.map +1 -0
- package/dist/vercel/internal/sdk.d.ts +173 -0
- package/dist/vercel/internal/sdk.d.ts.map +1 -0
- package/dist/vercel/internal/sdk.js +117 -0
- package/dist/vercel/internal/sdk.js.map +1 -0
- package/dist/vercel/runtime.d.ts +305 -0
- package/dist/vercel/runtime.d.ts.map +1 -0
- package/dist/vercel/runtime.js +1169 -0
- package/dist/vercel/runtime.js.map +1 -0
- package/docs/freestyle.md +110 -0
- package/package.json +35 -4
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal benchmark harness: ledger, cost guards, and measurement.
|
|
3
|
+
*
|
|
4
|
+
* This module is deliberately separate from any live credential. It contains
|
|
5
|
+
* no I/O of its own — it drives a `SandboxRuntime` that the caller supplies —
|
|
6
|
+
* so every guard below is unit-testable against a fake runtime, and the guards
|
|
7
|
+
* are proven *before* they are trusted with a real account.
|
|
8
|
+
*
|
|
9
|
+
* The safety model has three independent layers, and each one alone is
|
|
10
|
+
* sufficient to stop a runaway:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Ledger before use.** Every sandbox is recorded as *intended* before the
|
|
13
|
+
* create call is made, not after it returns. A create that times out or
|
|
14
|
+
* crashes mid-flight still leaves a record, so a sandbox that was actually
|
|
15
|
+
* provisioned can never be invisible to cleanup.
|
|
16
|
+
* 2. **Bounded cost.** A projected cost is computed for each sandbox from
|
|
17
|
+
* Modal's published rates and charged against a hard budget *before* the
|
|
18
|
+
* create. The run aborts rather than exceeding it.
|
|
19
|
+
* 3. **Verified cleanup.** Teardown runs in a `finally`, and afterwards each
|
|
20
|
+
* ledger entry is re-checked against the provider. Anything still present
|
|
21
|
+
* is reported as a leak rather than being assumed gone.
|
|
22
|
+
*/
|
|
23
|
+
import { MODAL_MIN_CPU_CORES } from "./config.js";
|
|
24
|
+
// --- published rates -------------------------------------------------------
|
|
25
|
+
/**
|
|
26
|
+
* Modal's published per-second rates, read from <https://modal.com/pricing> on
|
|
27
|
+
* 2026-08-21.
|
|
28
|
+
*
|
|
29
|
+
* Note the two cards. Modal bills **Sandboxes at roughly 3× its own Functions**,
|
|
30
|
+
* and the pricing page presents both in one table, so quoting the Function rate
|
|
31
|
+
* for a sandbox workload understates it threefold. Only the Sandbox card is
|
|
32
|
+
* correct for this adapter; the Function card is kept solely so the ratio can
|
|
33
|
+
* be asserted in a test rather than restated as a comment.
|
|
34
|
+
*
|
|
35
|
+
* Provider pricing is time-unstable. Re-verify before these decide anything.
|
|
36
|
+
*/
|
|
37
|
+
export const MODAL_RATES = {
|
|
38
|
+
/** Sandbox/Notebook rate, USD per **physical core** per second. */
|
|
39
|
+
sandboxCpuCoreSecondUsd: 0.00003942,
|
|
40
|
+
/** Sandbox/Notebook rate, USD per GiB of memory per second. */
|
|
41
|
+
sandboxMemoryGiBSecondUsd: 0.00000667,
|
|
42
|
+
/** Ordinary Function rate, USD per physical core per second. */
|
|
43
|
+
functionCpuCoreSecondUsd: 0.0000131,
|
|
44
|
+
/** Ordinary Function rate, USD per GiB of memory per second. */
|
|
45
|
+
functionMemoryGiBSecondUsd: 0.00000222,
|
|
46
|
+
/** Volume storage, USD per GiB per month. Not charged per second. */
|
|
47
|
+
volumeStorageGiBMonthUsd: 0.09,
|
|
48
|
+
/** Modal counts one physical core as this many vCPU. */
|
|
49
|
+
vcpuPerPhysicalCore: 2,
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Projected USD cost of one sandbox at Modal's **Sandbox** rate.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately not clamped or rounded: a budget guard that quietly rounds a
|
|
55
|
+
* projection down is a budget guard that can be walked past.
|
|
56
|
+
*/
|
|
57
|
+
export function estimateModalSandboxCostUsd(input) {
|
|
58
|
+
requireCpuAtLeastFloor(input.cpuCores);
|
|
59
|
+
requireFinitePositive(input.memoryGiB, "memoryGiB");
|
|
60
|
+
requireFiniteNonNegative(input.seconds, "seconds");
|
|
61
|
+
return (input.cpuCores * MODAL_RATES.sandboxCpuCoreSecondUsd
|
|
62
|
+
+ input.memoryGiB * MODAL_RATES.sandboxMemoryGiBSecondUsd) * input.seconds;
|
|
63
|
+
}
|
|
64
|
+
/** The same shape billed as an ordinary Function, for ratio reporting only. */
|
|
65
|
+
export function estimateModalFunctionCostUsd(input) {
|
|
66
|
+
requireCpuAtLeastFloor(input.cpuCores);
|
|
67
|
+
requireFinitePositive(input.memoryGiB, "memoryGiB");
|
|
68
|
+
requireFiniteNonNegative(input.seconds, "seconds");
|
|
69
|
+
return (input.cpuCores * MODAL_RATES.functionCpuCoreSecondUsd
|
|
70
|
+
+ input.memoryGiB * MODAL_RATES.functionMemoryGiBSecondUsd) * input.seconds;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A record of every sandbox this harness intended to create.
|
|
74
|
+
*
|
|
75
|
+
* "Intended" is the important word. Recording only successful creates would
|
|
76
|
+
* lose exactly the sandboxes most likely to leak: those whose create call
|
|
77
|
+
* timed out or was interrupted after the provider had already provisioned
|
|
78
|
+
* them. An entry is written before the attempt and updated after.
|
|
79
|
+
*/
|
|
80
|
+
export class ModalBenchLedger {
|
|
81
|
+
entries = [];
|
|
82
|
+
budgetUsd;
|
|
83
|
+
constructor(options) {
|
|
84
|
+
requireFinitePositive(options.budgetUsd, "budgetUsd");
|
|
85
|
+
this.budgetUsd = options.budgetUsd;
|
|
86
|
+
}
|
|
87
|
+
/** Total projected cost of everything recorded so far. */
|
|
88
|
+
committedUsd() {
|
|
89
|
+
return this.entries.reduce((sum, entry) => sum + entry.projectedCostUsd, 0);
|
|
90
|
+
}
|
|
91
|
+
remainingUsd() {
|
|
92
|
+
return this.budgetUsd - this.committedUsd();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Reserve budget and record intent. Throws *before* any provider call if the
|
|
96
|
+
* projection would breach the cap, so the guard cannot be raced by the
|
|
97
|
+
* create it is guarding.
|
|
98
|
+
*/
|
|
99
|
+
recordIntent(label, projectedCostUsd) {
|
|
100
|
+
requireFiniteNonNegative(projectedCostUsd, "projectedCostUsd");
|
|
101
|
+
const wouldCommit = this.committedUsd() + projectedCostUsd;
|
|
102
|
+
if (wouldCommit > this.budgetUsd) {
|
|
103
|
+
throw new ModalBudgetExceededError({
|
|
104
|
+
budgetUsd: this.budgetUsd,
|
|
105
|
+
committedUsd: this.committedUsd(),
|
|
106
|
+
requestedUsd: projectedCostUsd,
|
|
107
|
+
label,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const entry = { label, state: "intended", projectedCostUsd };
|
|
111
|
+
this.entries.push(entry);
|
|
112
|
+
return entry;
|
|
113
|
+
}
|
|
114
|
+
all() {
|
|
115
|
+
return this.entries;
|
|
116
|
+
}
|
|
117
|
+
/** Entries that may still correspond to a live provider resource. */
|
|
118
|
+
outstanding() {
|
|
119
|
+
return this.entries.filter((entry) => entry.state !== "verified-gone"
|
|
120
|
+
&& entry.state !== "create-failed");
|
|
121
|
+
}
|
|
122
|
+
leaked() {
|
|
123
|
+
return this.entries.filter((entry) => entry.state === "leaked");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** A create was refused because it would breach the run's cost cap. */
|
|
127
|
+
export class ModalBudgetExceededError extends Error {
|
|
128
|
+
budgetUsd;
|
|
129
|
+
committedUsd;
|
|
130
|
+
requestedUsd;
|
|
131
|
+
constructor(options) {
|
|
132
|
+
super(`Modal benchmark refused to create "${options.label}": projected $${options.requestedUsd.toFixed(6)} `
|
|
133
|
+
+ `on top of $${options.committedUsd.toFixed(6)} would exceed the $${options.budgetUsd.toFixed(6)} cap`);
|
|
134
|
+
this.name = "ModalBudgetExceededError";
|
|
135
|
+
this.budgetUsd = options.budgetUsd;
|
|
136
|
+
this.committedUsd = options.committedUsd;
|
|
137
|
+
this.requestedUsd = options.requestedUsd;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Teardown finished with provider resources still present. */
|
|
141
|
+
export class ModalLeakedSandboxError extends Error {
|
|
142
|
+
leaked;
|
|
143
|
+
constructor(leaked) {
|
|
144
|
+
super(`Modal benchmark leaked ${leaked.length} sandbox(es) that are still present after teardown: `
|
|
145
|
+
+ leaked.map((entry) => `${entry.label}=${entry.sandboxId ?? "unknown-id"}`).join(", "));
|
|
146
|
+
this.name = "ModalLeakedSandboxError";
|
|
147
|
+
this.leaked = leaked;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Linear-interpolated percentile over a sorted copy of `values`.
|
|
152
|
+
*
|
|
153
|
+
* Returns `null` for an empty input rather than 0, because "no samples" and
|
|
154
|
+
* "zero milliseconds" are different claims and the second one is a lie.
|
|
155
|
+
*/
|
|
156
|
+
export function percentile(values, p) {
|
|
157
|
+
if (values.length === 0) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (!Number.isFinite(p) || p < 0 || p > 100) {
|
|
161
|
+
throw new Error(`percentile p must be within [0, 100]; got ${String(p)}`);
|
|
162
|
+
}
|
|
163
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
164
|
+
if (sorted.length === 1) {
|
|
165
|
+
return sorted[0];
|
|
166
|
+
}
|
|
167
|
+
const rank = (p / 100) * (sorted.length - 1);
|
|
168
|
+
const lower = Math.floor(rank);
|
|
169
|
+
const upper = Math.ceil(rank);
|
|
170
|
+
if (lower === upper) {
|
|
171
|
+
return sorted[lower];
|
|
172
|
+
}
|
|
173
|
+
return sorted[lower] + (sorted[upper] - sorted[lower]) * (rank - lower);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Run the benchmark.
|
|
177
|
+
*
|
|
178
|
+
* Cleanup is unconditional and verified. Even when the run aborts on the
|
|
179
|
+
* budget guard or a create throws, teardown still runs over everything the
|
|
180
|
+
* ledger recorded, and the returned report names anything that survived.
|
|
181
|
+
*/
|
|
182
|
+
export async function runModalBenchmark(runtime, options) {
|
|
183
|
+
// Reject a non-integer instead of silently truncating: a caller who passes
|
|
184
|
+
// `count: 2.7` almost certainly typed a wrong number, and running 2 without
|
|
185
|
+
// saying so buys a benchmark for a shape that was never requested.
|
|
186
|
+
if (!Number.isFinite(options.count)
|
|
187
|
+
|| !Number.isInteger(options.count)
|
|
188
|
+
|| options.count < 1) {
|
|
189
|
+
throw new Error(`Modal benchmark count must be a positive integer; got ${String(options.count)}`);
|
|
190
|
+
}
|
|
191
|
+
const count = options.count;
|
|
192
|
+
// A zero-lifetime projection reserves $0 in the ledger but still creates
|
|
193
|
+
// sandboxes with a positive provider lifetime, so the budget guard cannot
|
|
194
|
+
// stop the run before it bills. Guard here where the value crosses into the
|
|
195
|
+
// guard rather than deep in the estimator.
|
|
196
|
+
if (!Number.isFinite(options.projectedLifetimeSeconds)
|
|
197
|
+
|| options.projectedLifetimeSeconds <= 0) {
|
|
198
|
+
throw new Error("Modal benchmark projectedLifetimeSeconds must be a finite, positive number of seconds "
|
|
199
|
+
+ "(and should cover the runtime's configured maxLifetimeMs, because a leaked "
|
|
200
|
+
+ `sandbox bills for its whole lifetime); got ${String(options.projectedLifetimeSeconds)}`);
|
|
201
|
+
}
|
|
202
|
+
const now = options.now ?? (() => Date.now());
|
|
203
|
+
const ledger = new ModalBenchLedger({ budgetUsd: options.budgetUsd });
|
|
204
|
+
const perSandboxUsd = estimateModalSandboxCostUsd({
|
|
205
|
+
cpuCores: options.shape.cpuCores,
|
|
206
|
+
memoryGiB: options.shape.memoryGiB,
|
|
207
|
+
seconds: options.projectedLifetimeSeconds,
|
|
208
|
+
});
|
|
209
|
+
const samples = [];
|
|
210
|
+
const labels = options.labels ?? {};
|
|
211
|
+
// Only sweep-by-label if the caller supplied labels *and* the runtime can
|
|
212
|
+
// list by them. Without both, a stray sandbox cannot be attributed to this
|
|
213
|
+
// run and the entry stays `create-failed` — the pre-existing shape.
|
|
214
|
+
const canReconcileByLabel = Object.keys(labels).length > 0
|
|
215
|
+
&& typeof runtime.findAllByLabels === "function";
|
|
216
|
+
const acquire = async (index) => {
|
|
217
|
+
const label = `bench-${index}`;
|
|
218
|
+
// Budget check and ledger write happen together, before any provider call.
|
|
219
|
+
const entry = ledger.recordIntent(label, perSandboxUsd);
|
|
220
|
+
const startedAt = now();
|
|
221
|
+
let handle;
|
|
222
|
+
try {
|
|
223
|
+
handle = await runtime.launch({ name: label, labels });
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
entry.error = errorMessage(error);
|
|
227
|
+
// Leave the entry `intended` when a label sweep can reconcile it:
|
|
228
|
+
// `launch` throwing does not prove the provider created nothing, and
|
|
229
|
+
// marking `create-failed` here would hide any sandbox that materialised
|
|
230
|
+
// after the client-side deadline fired. Teardown's sweep either
|
|
231
|
+
// reattaches the stray and destroys it, or promotes the entry to
|
|
232
|
+
// `create-failed` once absence has been checked.
|
|
233
|
+
if (!canReconcileByLabel) {
|
|
234
|
+
entry.state = "create-failed";
|
|
235
|
+
}
|
|
236
|
+
samples.push({ label, createMs: now() - startedAt, error: entry.error });
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const createMs = now() - startedAt;
|
|
240
|
+
entry.state = "created";
|
|
241
|
+
entry.sandboxId = handle.id;
|
|
242
|
+
let firstExecMs;
|
|
243
|
+
let execError;
|
|
244
|
+
if (options.measureFirstExec) {
|
|
245
|
+
const execStartedAt = now();
|
|
246
|
+
try {
|
|
247
|
+
const result = await runtime.runScript(handle, {
|
|
248
|
+
command: options.firstExecCommand ?? "echo modal-bench-ready",
|
|
249
|
+
});
|
|
250
|
+
// A canary that exits nonzero is a failure, not a latency sample.
|
|
251
|
+
// Recording `firstExecMs` for a failed command would let the harness
|
|
252
|
+
// report a green measurement for a command that never worked.
|
|
253
|
+
if (result.exitCode === 0) {
|
|
254
|
+
firstExecMs = now() - execStartedAt;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
execError = `first exec exited with code ${String(result.exitCode ?? "unknown")}`;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
execError = errorMessage(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
samples.push({
|
|
265
|
+
label,
|
|
266
|
+
createMs,
|
|
267
|
+
sandboxId: handle.id,
|
|
268
|
+
...(firstExecMs === undefined ? {} : { firstExecMs }),
|
|
269
|
+
...(execError === undefined ? {} : { error: execError }),
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
let abortError;
|
|
273
|
+
try {
|
|
274
|
+
if (options.concurrent) {
|
|
275
|
+
// `allSettled`, not `all`: one create failing must not skip teardown for
|
|
276
|
+
// the others, which would be exactly how a concurrency test leaks.
|
|
277
|
+
const results = await Promise.allSettled(Array.from({ length: count }, (_unused, index) => acquire(index)));
|
|
278
|
+
const rejected = results.find((result) => result.status === "rejected");
|
|
279
|
+
if (rejected && rejected.status === "rejected") {
|
|
280
|
+
abortError = rejected.reason;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
for (let index = 0; index < count; index += 1) {
|
|
285
|
+
await acquire(index);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
// A budget breach aborts the run but must never skip teardown.
|
|
291
|
+
abortError = error;
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
await teardown(runtime, ledger, labels);
|
|
295
|
+
}
|
|
296
|
+
const leaked = ledger.leaked();
|
|
297
|
+
const report = {
|
|
298
|
+
requested: count,
|
|
299
|
+
succeeded: samples.filter((sample) => sample.sandboxId !== undefined && !sample.error).length,
|
|
300
|
+
failed: samples.filter((sample) => sample.error !== undefined).length,
|
|
301
|
+
samples,
|
|
302
|
+
createMsP50: percentile(successfulCreates(samples), 50),
|
|
303
|
+
createMsP95: percentile(successfulCreates(samples), 95),
|
|
304
|
+
firstExecMsP50: percentile(firstExecs(samples), 50),
|
|
305
|
+
firstExecMsP95: percentile(firstExecs(samples), 95),
|
|
306
|
+
projectedCostUsd: ledger.committedUsd(),
|
|
307
|
+
ledger: ledger.all(),
|
|
308
|
+
leaked,
|
|
309
|
+
};
|
|
310
|
+
if (leaked.length > 0) {
|
|
311
|
+
// Loud by design. A silent leak on a metered provider is the one failure
|
|
312
|
+
// mode this harness exists to prevent.
|
|
313
|
+
throw Object.assign(new ModalLeakedSandboxError(leaked), { report });
|
|
314
|
+
}
|
|
315
|
+
if (abortError !== undefined) {
|
|
316
|
+
throw Object.assign(toError(abortError), { report });
|
|
317
|
+
}
|
|
318
|
+
return report;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Destroy everything the ledger knows about, then prove it is gone.
|
|
322
|
+
*
|
|
323
|
+
* Every entry is attempted even if an earlier one throws, because stopping at
|
|
324
|
+
* the first failure would strand the rest.
|
|
325
|
+
*/
|
|
326
|
+
async function teardown(runtime, ledger, labels) {
|
|
327
|
+
// Pass 1: destroy sandboxes we already have ids for.
|
|
328
|
+
for (const entry of ledger.outstanding()) {
|
|
329
|
+
if (entry.sandboxId === undefined) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
await runtime.destroy({ id: entry.sandboxId });
|
|
334
|
+
entry.state = "destroyed";
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
entry.state = "destroy-failed";
|
|
338
|
+
entry.error = errorMessage(error);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Pass 2: reconcile launches that threw without returning a handle. A
|
|
342
|
+
// `launch` rejection does not prove the provider created nothing — a
|
|
343
|
+
// sandbox that landed just after the client-side deadline fired would
|
|
344
|
+
// otherwise leak invisibly. Sweep by the caller's labels, associate any
|
|
345
|
+
// stray with an outstanding no-id entry, and destroy it. This is why the
|
|
346
|
+
// acquire loop leaves such entries `intended` rather than `create-failed`.
|
|
347
|
+
const unaccounted = ledger.outstanding().filter((entry) => entry.sandboxId === undefined);
|
|
348
|
+
const canSweep = unaccounted.length > 0
|
|
349
|
+
&& Object.keys(labels).length > 0
|
|
350
|
+
&& typeof runtime.findAllByLabels === "function";
|
|
351
|
+
if (canSweep) {
|
|
352
|
+
const knownIds = new Set(ledger.all()
|
|
353
|
+
.map((entry) => entry.sandboxId)
|
|
354
|
+
.filter((id) => id !== undefined));
|
|
355
|
+
let strays = [];
|
|
356
|
+
try {
|
|
357
|
+
strays = (await runtime.findAllByLabels(labels))
|
|
358
|
+
.filter((handle) => !knownIds.has(handle.id));
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
// A sweep failure is not fatal, but it does mean any remaining no-id
|
|
362
|
+
// entries have to fall through as unverified — see below.
|
|
363
|
+
for (const entry of unaccounted) {
|
|
364
|
+
entry.error = errorMessage(error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
for (const entry of unaccounted) {
|
|
368
|
+
const match = strays.shift();
|
|
369
|
+
if (match === undefined) {
|
|
370
|
+
// Sweep ran and no matching stray exists: this create truly failed.
|
|
371
|
+
entry.state = "create-failed";
|
|
372
|
+
entry.error ??= "create rejected and no stray sandbox was found by label sweep";
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
entry.sandboxId = match.id;
|
|
376
|
+
try {
|
|
377
|
+
await runtime.destroy(match);
|
|
378
|
+
entry.state = "destroyed";
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
entry.state = "destroy-failed";
|
|
382
|
+
entry.error = errorMessage(error);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
for (const entry of unaccounted) {
|
|
388
|
+
entry.state = "leaked";
|
|
389
|
+
entry.error ??= "create outcome unknown: no sandbox id was ever observed "
|
|
390
|
+
+ "and no label sweep was possible";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// Pass 3: verify. `destroy` returning is not proof; a re-lookup reporting
|
|
394
|
+
// absence is.
|
|
395
|
+
for (const entry of ledger.outstanding()) {
|
|
396
|
+
if (entry.sandboxId === undefined) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (typeof runtime.getById !== "function") {
|
|
400
|
+
// Without reattach there is no way to prove absence, so it is not
|
|
401
|
+
// claimed. The entry stays outstanding rather than being upgraded.
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const found = await runtime.getById(entry.sandboxId);
|
|
406
|
+
entry.state = found === null ? "verified-gone" : "leaked";
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
entry.state = "leaked";
|
|
410
|
+
entry.error = errorMessage(error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// --- helpers ---------------------------------------------------------------
|
|
415
|
+
function successfulCreates(samples) {
|
|
416
|
+
return samples.filter((s) => s.sandboxId !== undefined).map((s) => s.createMs);
|
|
417
|
+
}
|
|
418
|
+
function firstExecs(samples) {
|
|
419
|
+
return samples
|
|
420
|
+
.map((s) => s.firstExecMs)
|
|
421
|
+
.filter((value) => value !== undefined);
|
|
422
|
+
}
|
|
423
|
+
function errorMessage(error) {
|
|
424
|
+
return error instanceof Error ? error.message : String(error);
|
|
425
|
+
}
|
|
426
|
+
function toError(error) {
|
|
427
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
428
|
+
}
|
|
429
|
+
function requireFinitePositive(value, field) {
|
|
430
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
431
|
+
throw new Error(`Modal benchmark ${field} must be a finite, positive number; got ${String(value)}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function requireFiniteNonNegative(value, field) {
|
|
435
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
436
|
+
throw new Error(`Modal benchmark ${field} must be a finite, non-negative number; got ${String(value)}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Modal charges as if the reservation were at least its per-container floor.
|
|
441
|
+
* Accepting a below-floor CPU value in the estimator would understate spend
|
|
442
|
+
* and let the budget guard be walked past.
|
|
443
|
+
*/
|
|
444
|
+
function requireCpuAtLeastFloor(cpuCores) {
|
|
445
|
+
if (!Number.isFinite(cpuCores) || cpuCores < MODAL_MIN_CPU_CORES) {
|
|
446
|
+
throw new Error(`Modal benchmark cpuCores must be a finite number >= ${MODAL_MIN_CPU_CORES} `
|
|
447
|
+
+ `physical cores (Modal's per-container floor); got ${String(cpuCores)}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
//# sourceMappingURL=bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bench.js","sourceRoot":"","sources":["../../src/modal/bench.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK-free capability metadata for the Modal adapter.
|
|
3
|
+
*
|
|
4
|
+
* House rule: a behavioral claim stays `false` until a live probe against the
|
|
5
|
+
* pinned SDK establishes it. "The SDK has a method for it" is not evidence —
|
|
6
|
+
* a method can exist and degrade, and a capability means *reachable through
|
|
7
|
+
* this package's port*, not *present in the vendor surface*.
|
|
8
|
+
*/
|
|
9
|
+
export declare const modalSandboxCapabilities: {
|
|
10
|
+
/**
|
|
11
|
+
* Modal genuinely has server-side label search: `sandboxes.list({ tags })`
|
|
12
|
+
* returns only sandboxes carrying all the given tags, and the adapter routes
|
|
13
|
+
* every lookup through it. That is a real warm-lease implementation, unlike
|
|
14
|
+
* providers where `findAllByLabels` degrades to `[]`.
|
|
15
|
+
*
|
|
16
|
+
* It is nevertheless declared `false` until the live canary proves that a
|
|
17
|
+
* tagged sandbox is actually returned by a tag-filtered list. Promoting it
|
|
18
|
+
* on the strength of a type signature is exactly the inference this field
|
|
19
|
+
* exists to prevent, and `false` fails in the safe direction: the
|
|
20
|
+
* orchestrator declines to rely on warm leases rather than leaking them.
|
|
21
|
+
*/
|
|
22
|
+
readonly warmLease: false;
|
|
23
|
+
/**
|
|
24
|
+
* Permanently false, and not pending verification.
|
|
25
|
+
*
|
|
26
|
+
* Modal exposes no stop/start for a Sandbox. `terminate()` is the only
|
|
27
|
+
* lifecycle transition and it is terminal — there is no suspend, resume, or
|
|
28
|
+
* wake. The adapter therefore omits `start`/`stop` entirely rather than
|
|
29
|
+
* shipping no-ops, and this flag records the structural fact for the
|
|
30
|
+
* capability resolver, which cannot see an absent method.
|
|
31
|
+
*/
|
|
32
|
+
readonly lifecycle: false;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Bootstrap-plane capabilities. Note this is `RuntimeCapabilities` from
|
|
36
|
+
* `../types.js` — the live in-sandbox plane — and NOT the orchestration-plane
|
|
37
|
+
* `SandboxRuntimeCapabilities`. The two are deliberately distinct.
|
|
38
|
+
*/
|
|
39
|
+
export declare const modalWorkflowCapabilities: {
|
|
40
|
+
/**
|
|
41
|
+
* Modal supports PTY on both `create` and `exec` (`pty: true`, which
|
|
42
|
+
* multiplexes stderr into stdout). This package's port exposes no PTY
|
|
43
|
+
* operation, so it is not reachable here.
|
|
44
|
+
*/
|
|
45
|
+
readonly pty: false;
|
|
46
|
+
/**
|
|
47
|
+
* Modal exposes real filesystem snapshots — `snapshotFilesystem()` and
|
|
48
|
+
* `snapshotDirectory(path)`, both returning an `Image` reusable as a create
|
|
49
|
+
* source (30-day default TTL, `ttlMs: null` to retain indefinitely). This
|
|
50
|
+
* package's port exposes no snapshot operation, so the capability is not
|
|
51
|
+
* reachable through it. Documented here so the surface is not lost.
|
|
52
|
+
*/
|
|
53
|
+
readonly snapshots: false;
|
|
54
|
+
/** Modal Sandboxes are gVisor-isolated containers with their own filesystem. */
|
|
55
|
+
readonly isolation: "strong";
|
|
56
|
+
/** A sandbox id survives the client: `sandboxes.fromId(id)` re-resolves it. */
|
|
57
|
+
readonly persistentHandle: true;
|
|
58
|
+
/**
|
|
59
|
+
* `exec()` returns live `stdout`/`stderr` as `ReadableStream`s, so Modal can
|
|
60
|
+
* genuinely stream. The adapter drains them to completion to satisfy the
|
|
61
|
+
* port's buffered `RunScriptResult`, and the port exposes no streaming
|
|
62
|
+
* surface, so this is false *for this port* while true of the provider.
|
|
63
|
+
*/
|
|
64
|
+
readonly streamingLogs: false;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Cells proven by a live run against Modal. Everything starts `false` and is
|
|
68
|
+
* promoted only by an observation recorded in the adapter's docs, never by
|
|
69
|
+
* reading the SDK's types.
|
|
70
|
+
*/
|
|
71
|
+
export type ModalObservedCapabilities = {
|
|
72
|
+
/** A terminated sandbox verifiably left the account, checked by re-lookup. */
|
|
73
|
+
cleanupVerified: boolean;
|
|
74
|
+
/** `snapshotFilesystem` produced an Image that booted a new sandbox. */
|
|
75
|
+
snapshotCapture: boolean;
|
|
76
|
+
/** Tag-filtered `list()` returned a sandbox we tagged at create. */
|
|
77
|
+
warmLease: boolean;
|
|
78
|
+
/** `fromId` re-resolved a sandbox created by a different client instance. */
|
|
79
|
+
reattach: boolean;
|
|
80
|
+
/** stop -> settled -> start -> exec succeeded. Structurally impossible on Modal. */
|
|
81
|
+
lifecycle: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* A sandbox can be kept alive indefinitely with no termination deadline.
|
|
84
|
+
*
|
|
85
|
+
* Structurally false on Modal, like {@link lifecycle}, and for the same kind
|
|
86
|
+
* of reason: *every* Modal Sandbox carries a maximum lifetime. It defaults to
|
|
87
|
+
* five minutes, it can be raised only as far as 24 hours, and the provider
|
|
88
|
+
* terminates the sandbox when it elapses. There is no "no deadline" setting.
|
|
89
|
+
* Past 24 hours Modal's own guidance is to snapshot the filesystem and
|
|
90
|
+
* restore it into a *new* sandbox, which is a rebuild rather than a
|
|
91
|
+
* continuation. No live run can promote this.
|
|
92
|
+
*/
|
|
93
|
+
neverIdle: boolean;
|
|
94
|
+
/** A sandbox outlived Modal's 5-minute default because we set maxLifetimeMs. */
|
|
95
|
+
lifetimeOverride: boolean;
|
|
96
|
+
/** Concurrent creates all reached ready without provider-side throttling. */
|
|
97
|
+
concurrencyCeiling: boolean;
|
|
98
|
+
};
|
|
99
|
+
/** Nothing promoted yet: no live run has happened. Awaiting credentials. */
|
|
100
|
+
export declare const modalObservedCapabilities: ModalObservedCapabilities;
|
|
101
|
+
/**
|
|
102
|
+
* Cells that are **settled structural facts**, not pending observations.
|
|
103
|
+
*
|
|
104
|
+
* The distinction matters for review: everything else in
|
|
105
|
+
* {@link modalObservedCapabilities} is `false` only because no live run has
|
|
106
|
+
* happened yet and will flip when one does. These two are `false` because the
|
|
107
|
+
* provider cannot do the thing at all, so a future canary must not "promote"
|
|
108
|
+
* them and a future reader must not try to "fix" them.
|
|
109
|
+
*/
|
|
110
|
+
export declare const MODAL_STRUCTURALLY_FALSE: readonly ["lifecycle", "neverIdle"];
|
|
111
|
+
/**
|
|
112
|
+
* Structured capability modes for the Modal adapter.
|
|
113
|
+
*
|
|
114
|
+
* Every cell below restates, in the type system, something this file already
|
|
115
|
+
* documented in prose. That is the whole reason to fill them in: the booleans
|
|
116
|
+
* above cannot distinguish "Modal cannot do this" from "Modal can, but our port
|
|
117
|
+
* exposes no operation reaching it" from "nobody has checked yet", and this
|
|
118
|
+
* adapter is the one that had to work around that by hand — see
|
|
119
|
+
* {@link MODAL_STRUCTURALLY_FALSE}.
|
|
120
|
+
*
|
|
121
|
+
* Note what is deliberately *not* here: `warmLease` stays a `false` boolean
|
|
122
|
+
* pending the live canary. Modes describe the shape of a capability, not its
|
|
123
|
+
* verification state, so a mode is not a back door for promoting a cell that
|
|
124
|
+
* the house rule keeps unproven.
|
|
125
|
+
*/
|
|
126
|
+
export declare const modalCapabilityModes: {
|
|
127
|
+
/**
|
|
128
|
+
* `buffered`, not `separate-streams`.
|
|
129
|
+
*
|
|
130
|
+
* Modal's `exec()` does return live `stdout`/`stderr` as separate
|
|
131
|
+
* `ReadableStream`s, and `runScript` does hand back separated `stdout` and
|
|
132
|
+
* `stderr` fields. But both streaming members of the union require output to
|
|
133
|
+
* be *streamed live*, and the adapter drains both pipes to completion with
|
|
134
|
+
* `readText()` before it returns anything. Separated-after-the-fact is still
|
|
135
|
+
* buffered. Declaring `separate-streams` would promise callers an incremental
|
|
136
|
+
* channel this port does not have.
|
|
137
|
+
*/
|
|
138
|
+
readonly outputStreams: "buffered";
|
|
139
|
+
/**
|
|
140
|
+
* Anything written is lost when the sandbox goes away, and on Modal it always
|
|
141
|
+
* goes away: `terminate()` is the only lifecycle transition and it is
|
|
142
|
+
* terminal. There is no stop/start pair for state to survive across, so
|
|
143
|
+
* `persistent` cannot apply.
|
|
144
|
+
*/
|
|
145
|
+
readonly filesystem: "ephemeral";
|
|
146
|
+
/**
|
|
147
|
+
* The structural fact behind `neverIdle: false` in
|
|
148
|
+
* {@link MODAL_STRUCTURALLY_FALSE}, now stated in the type rather than in a
|
|
149
|
+
* hand-maintained list. *Every* Modal Sandbox carries a maximum lifetime —
|
|
150
|
+
* five minutes by default, 24 hours at the ceiling (`MODAL_MAX_LIFETIME_MS`),
|
|
151
|
+
* and the provider terminates it when that elapses. There is no "no deadline"
|
|
152
|
+
* setting, so no live run can ever move this.
|
|
153
|
+
*
|
|
154
|
+
* `deadline` says that on its own; per the union's own note, a provider that
|
|
155
|
+
* always terminates at a deadline cannot offer a never-idle tier, so this
|
|
156
|
+
* needs no separate `unsupported`.
|
|
157
|
+
*/
|
|
158
|
+
readonly lifetime: "deadline";
|
|
159
|
+
/**
|
|
160
|
+
* `not-exposed`, emphatically not `unsupported`: Modal supports PTY on both
|
|
161
|
+
* `create` and `exec` (`pty: true`). This package's port declares no PTY
|
|
162
|
+
* operation, so it is unreachable *here*. It moves if someone adds one — and
|
|
163
|
+
* `isPendingEvidence()` correctly reports false for it, because no canary
|
|
164
|
+
* against Modal can change a fact about our own port surface.
|
|
165
|
+
*/
|
|
166
|
+
readonly interactive: "not-exposed";
|
|
167
|
+
/**
|
|
168
|
+
* Also `not-exposed`. Modal has real filesystem snapshots —
|
|
169
|
+
* `snapshotFilesystem()` and `snapshotDirectory(path)`, both returning an
|
|
170
|
+
* `Image` reusable as a create source. The port exposes no snapshot
|
|
171
|
+
* operation, so the capability is not reachable through it.
|
|
172
|
+
*
|
|
173
|
+
* This is exactly the cell the absence vocabulary was added to protect:
|
|
174
|
+
* `modalObservedCapabilities.snapshotCapture` is a *pending* observation
|
|
175
|
+
* about the provider, while this is a *settled* fact about our port. A canary
|
|
176
|
+
* may promote that one; it must never promote this one.
|
|
177
|
+
*/
|
|
178
|
+
readonly snapshots: "not-exposed";
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/modal/capabilities.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB;IACnC;;;;;;;;;;;OAWG;;IAEH;;;;;;;;OAQG;;CAEkD,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;IACpC;;;;OAIG;;IAEH;;;;;;OAMG;;IAEH,gFAAgF;;IAEhF,+EAA+E;;IAE/E;;;;;OAKG;;CAEmC,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,eAAe,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAC;IAClB,oFAAoF;IACpF,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;OAUG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,gFAAgF;IAChF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,EAAE,yBASvC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,qCAG8B,CAAC;AAEpE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB;IAC/B;;;;;;;;;;OAUG;;IAEH;;;;;OAKG;;IAEH;;;;;;;;;;;OAWG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;;;;OAUG;;CAEsC,CAAC"}
|