@sandboxrouter/sdk 0.2.0 → 0.3.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/README.md +229 -50
- package/dist/bench.js +4 -4
- package/dist/catalog.d.ts +159 -0
- package/dist/catalog.js +450 -15
- package/dist/check.d.ts +28 -0
- package/dist/check.js +185 -0
- package/dist/cli.js +171 -12
- package/dist/computers.d.ts +55 -13
- package/dist/computers.js +132 -6
- package/dist/conformance.d.ts +46 -0
- package/dist/conformance.js +65 -0
- package/dist/cost.d.ts +67 -0
- package/dist/cost.js +153 -0
- package/dist/gc.d.ts +29 -0
- package/dist/gc.js +54 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.js +28 -2
- package/dist/leaderboard.d.ts +6 -0
- package/dist/leaderboard.js +5 -4
- package/dist/ledger.d.ts +38 -0
- package/dist/ledger.js +89 -0
- package/dist/providers/agent37.d.ts +40 -0
- package/dist/providers/agent37.js +224 -0
- package/dist/providers/agentcore.d.ts +71 -0
- package/dist/providers/agentcore.js +446 -0
- package/dist/providers/alibaba.d.ts +18 -0
- package/dist/providers/alibaba.js +106 -0
- package/dist/providers/azure.d.ts +43 -0
- package/dist/providers/azure.js +330 -0
- package/dist/providers/beam.d.ts +11 -0
- package/dist/providers/beam.js +179 -0
- package/dist/providers/blaxel.d.ts +3 -1
- package/dist/providers/blaxel.js +32 -22
- package/dist/providers/boat.js +8 -3
- package/dist/providers/cloudflare.js +8 -3
- package/dist/providers/codesandbox.d.ts +59 -0
- package/dist/providers/codesandbox.js +287 -0
- package/dist/providers/daytona.d.ts +25 -4
- package/dist/providers/daytona.js +449 -28
- package/dist/providers/dedalus.d.ts +12 -0
- package/dist/providers/dedalus.js +244 -0
- package/dist/providers/deno.d.ts +50 -0
- package/dist/providers/deno.js +170 -0
- package/dist/providers/docker.js +11 -5
- package/dist/providers/e2b.d.ts +70 -3
- package/dist/providers/e2b.js +376 -8
- package/dist/providers/exedev.js +17 -5
- package/dist/providers/freestyle.d.ts +49 -0
- package/dist/providers/freestyle.js +261 -0
- package/dist/providers/hopx.js +27 -18
- package/dist/providers/instabox.d.ts +76 -0
- package/dist/providers/instabox.js +360 -0
- package/dist/providers/koyeb.d.ts +71 -0
- package/dist/providers/koyeb.js +379 -0
- package/dist/providers/lambda.d.ts +51 -0
- package/dist/providers/lambda.js +467 -0
- package/dist/providers/langsmith.d.ts +16 -0
- package/dist/providers/langsmith.js +149 -0
- package/dist/providers/limrun.d.ts +77 -0
- package/dist/providers/limrun.js +626 -0
- package/dist/providers/maritime.d.ts +5 -1
- package/dist/providers/maritime.js +39 -8
- package/dist/providers/mobilerun.d.ts +44 -0
- package/dist/providers/mobilerun.js +417 -0
- package/dist/providers/modal.js +12 -4
- package/dist/providers/morph.js +12 -4
- package/dist/providers/northflank.js +11 -13
- package/dist/providers/novita.d.ts +12 -0
- package/dist/providers/novita.js +81 -0
- package/dist/providers/openscale.d.ts +17 -0
- package/dist/providers/openscale.js +87 -0
- package/dist/providers/orgo.d.ts +45 -0
- package/dist/providers/orgo.js +414 -0
- package/dist/providers/sail.d.ts +32 -0
- package/dist/providers/sail.js +372 -0
- package/dist/providers/superserve.d.ts +25 -0
- package/dist/providers/superserve.js +337 -0
- package/dist/providers/tensorlake.js +17 -14
- package/dist/providers/upstash.d.ts +30 -0
- package/dist/providers/upstash.js +258 -0
- package/dist/providers/vercel.js +14 -3
- package/dist/providers/workspaces.d.ts +60 -0
- package/dist/providers/workspaces.js +662 -0
- package/dist/router.d.ts +41 -5
- package/dist/router.js +179 -23
- package/dist/server.d.ts +1 -1
- package/dist/server.js +22 -3
- package/dist/types.d.ts +13 -5
- package/dist/util.d.ts +28 -1
- package/dist/util.js +56 -7
- package/package.json +81 -5
package/dist/computers.js
CHANGED
|
@@ -34,21 +34,147 @@ export class RoutedComputer {
|
|
|
34
34
|
writeFile(path, content) {
|
|
35
35
|
return this.c.writeFile(path, content);
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
/** What this computer's provider supports beyond the core actions. */
|
|
38
|
+
get supports() {
|
|
39
|
+
return { viewer: typeof this.c.viewer === "function", sleep: typeof this.c.sleep === "function" };
|
|
40
|
+
}
|
|
41
|
+
async viewer(mode, opts) {
|
|
42
|
+
if (!this.c.viewer)
|
|
43
|
+
throw new RouterError(`${this.provider} has no shareable live view`);
|
|
38
44
|
return this.c.viewer(mode, opts);
|
|
39
45
|
}
|
|
40
|
-
sleep() {
|
|
41
|
-
|
|
46
|
+
async sleep() {
|
|
47
|
+
if (!this.c.sleep)
|
|
48
|
+
throw new RouterError(`${this.provider} can't sleep a computer; delete it instead`);
|
|
49
|
+
await this.c.sleep();
|
|
42
50
|
}
|
|
43
51
|
delete() {
|
|
44
52
|
return this.c.delete();
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
|
-
|
|
55
|
+
export const COMPUTER_ENTRIES = [
|
|
56
|
+
{
|
|
57
|
+
id: "maritime",
|
|
58
|
+
name: "Maritime",
|
|
59
|
+
os: { linux: 0.12, windows: 0.18, android: 0.15 },
|
|
60
|
+
note: "per second while awake; Starter $20/mo includes $20 usage; asleep pays $0.08/GB-month of disk; Windows needs Growth $100/mo incl. $100 usage",
|
|
61
|
+
source: "https://maritime.sh/pricing",
|
|
62
|
+
checked: "2026-11-05",
|
|
63
|
+
takeover: true,
|
|
64
|
+
size: "2 vCPU · 4 GB",
|
|
65
|
+
credentials: [["MARITIME_API_KEY"]],
|
|
66
|
+
docs: "https://maritime.sh/docs/computers",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "daytona",
|
|
70
|
+
name: "Daytona",
|
|
71
|
+
// (1 vCPU x $0.0504 + 1 GiB x $0.0162) per hour, billed per second; 3 GiB disk is inside the 5 GiB free
|
|
72
|
+
os: { linux: 0.0666 },
|
|
73
|
+
note: "per second while running; default container snapshot is 1 vCPU / 1 GiB / 3 GiB (daytona-small in daytona.io/docs/en/sandboxes), larger snapshots cost more; stopped keeps the disk ($0.000108/GiB-h past 5 GiB); live view is a signed noVNC preview URL, watch mode is client-side view-only; Windows needs a Tier 3 org, not routed",
|
|
74
|
+
source: "https://www.daytona.io/pricing",
|
|
75
|
+
checked: "2026-11-04",
|
|
76
|
+
takeover: true,
|
|
77
|
+
size: "1 vCPU · 1 GB",
|
|
78
|
+
credentials: [["DAYTONA_API_KEY"]],
|
|
79
|
+
docs: "https://www.daytona.io/docs/en/computer-use",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "e2b",
|
|
83
|
+
name: "E2B",
|
|
84
|
+
// (8 vCPU x $0.000014 + 8 GiB x $0.0000045) per second x 3600
|
|
85
|
+
os: { linux: 0.5328 },
|
|
86
|
+
note: "per second while running; the public `desktop` template is 8 vCPU / 8 GiB (cpu_count=8, memory_mb=8192 in github.com/e2b-dev/desktop template/build_prod.py); live view is noVNC with a password in the URL, watch mode is client-side view-only; Hobby caps a session at 1 h, Pro at 24 h",
|
|
87
|
+
source: "https://e2b.dev/pricing",
|
|
88
|
+
checked: "2026-11-04",
|
|
89
|
+
takeover: true,
|
|
90
|
+
size: "8 vCPU · 8 GB",
|
|
91
|
+
credentials: [["E2B_API_KEY"]],
|
|
92
|
+
docs: "https://docs.e2b.dev/use-cases/computer-use",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
COMPUTER_ENTRIES.push({
|
|
96
|
+
id: "orgo",
|
|
97
|
+
name: "Orgo",
|
|
98
|
+
os: { linux: 0.055 },
|
|
99
|
+
planOnly: ["windows"],
|
|
100
|
+
size: "2 vCPU · 8 GB",
|
|
101
|
+
note: "Medium Linux by the hour from credit, billed per second; stopped pays only its 40 GB disk. Other sizes need a paid plan (Starter $29/mo). Windows is not hourly: Scale plan ($399/mo) or a $15/mo licence plus a Medium. The live view is a VNC websockify URL whose token is full control, for a noVNC client",
|
|
102
|
+
source: "https://www.orgo.ai/pricing.md",
|
|
103
|
+
checked: "2026-11-04",
|
|
104
|
+
takeover: false,
|
|
105
|
+
credentials: [["ORGO_API_KEY"]],
|
|
106
|
+
docs: "https://docs.orgo.ai/api-reference/introduction",
|
|
107
|
+
});
|
|
108
|
+
COMPUTER_ENTRIES.push({
|
|
109
|
+
id: "mobilerun",
|
|
110
|
+
name: "Mobilerun",
|
|
111
|
+
os: { android: 1.8 },
|
|
112
|
+
size: "Android cloud phone (virtual, EU)",
|
|
113
|
+
note: "$0.03 per device minute past the plan allowance (Developer $20/mo incl. 600 min, Startup $99, Scaleup $299); Free has 60 min/month; stopped phones keep apps and data and bill no minutes; live view is a WebRTC stream for @mobilerun/react, always controllable; no shell",
|
|
114
|
+
source: "https://mobilerun.ai/pricing",
|
|
115
|
+
checked: "2026-11-04",
|
|
116
|
+
takeover: true,
|
|
117
|
+
credentials: [["MOBILERUN_CLOUD_API_KEY"]],
|
|
118
|
+
docs: "https://docs.mobilerun.ai",
|
|
119
|
+
}, {
|
|
120
|
+
id: "limrun",
|
|
121
|
+
name: "Limrun",
|
|
122
|
+
os: { android: 3.0, ios: 6.0 },
|
|
123
|
+
size: "Android 15 phone emulator / iPhone simulator",
|
|
124
|
+
note: "per-minute credits (Android 1/min, iOS 2/min); displayed hourly prices use paid overage $0.05/credit; optional no-subscription Trial overage $0.06/credit ($3.60/h Android, $7.20/h iOS); Trial 120 one-time credits, Entry $20/mo incl. 500, Individual $200/mo incl. 5,000; no pause, idle devices deleted at timeout; interactive stream; iOS has no shell",
|
|
125
|
+
source: "https://limrun.com/pricing",
|
|
126
|
+
checked: "2026-11-05",
|
|
127
|
+
takeover: true,
|
|
128
|
+
credentials: [["LIM_API_KEY"]],
|
|
129
|
+
docs: "https://docs.limrun.com/docs/reference/sdk.md",
|
|
130
|
+
});
|
|
131
|
+
COMPUTER_ENTRIES.push({
|
|
132
|
+
id: "aws-workspaces",
|
|
133
|
+
name: "AWS WorkSpaces (agent access)",
|
|
134
|
+
// stream.standard.medium (AWS's own agent-access example size) + the $0.05/h MCP session fee, us-east-1
|
|
135
|
+
os: { windows: 0.15, linux: 0.138 },
|
|
136
|
+
size: "2 vCPU · 4 GB (stream.standard.medium)",
|
|
137
|
+
note: "us-east-1, Always-On/On-Demand fleet you provision once (fleet + stack), plus $0.05/h MCP session fee (first 10 h per region per month free); Windows adds $6.42 per AI-agent user per month; watch link is truly view-only but a person can't take control; no shell or files",
|
|
138
|
+
source: "https://aws.amazon.com/workspaces/applications/pricing/",
|
|
139
|
+
checked: "2026-11-04",
|
|
140
|
+
takeover: false,
|
|
141
|
+
credentials: [["AWS_REGION", "WORKSPACES_AGENT_STACK", "WORKSPACES_AGENT_FLEET"]],
|
|
142
|
+
docs: "https://docs.aws.amazon.com/appstream2/latest/developerguide/agent-access-mcp-server.html",
|
|
143
|
+
});
|
|
144
|
+
export function computerCatalog() {
|
|
145
|
+
return [...COMPUTER_ENTRIES].sort((a, b) => a.name.localeCompare(b.name));
|
|
146
|
+
}
|
|
147
|
+
export function computerHourly(id, os) {
|
|
148
|
+
return COMPUTER_ENTRIES.find((e) => e.id === id)?.os[os];
|
|
149
|
+
}
|
|
150
|
+
/** The order a computer create would try, with the reason any provider is skipped. */
|
|
151
|
+
export function planComputer(providers, os = "linux", prefer) {
|
|
152
|
+
const ids = prefer ?? providers.map((p) => p.id);
|
|
153
|
+
const list = ids.map((id) => {
|
|
154
|
+
const p = providers.find((x) => x.id === id);
|
|
155
|
+
const c = { provider: id, estimatedHourlyUsd: computerHourly(id, os) };
|
|
156
|
+
if (!p)
|
|
157
|
+
c.skip = "unknown provider";
|
|
158
|
+
else if (!p.computerOs.includes(os))
|
|
159
|
+
c.skip = `no ${os} computers`;
|
|
160
|
+
else if (!p.configured())
|
|
161
|
+
c.skip = "not configured";
|
|
162
|
+
return c;
|
|
163
|
+
});
|
|
164
|
+
if (!prefer) {
|
|
165
|
+
list.sort((a, b) => (a.estimatedHourlyUsd ?? Infinity) - (b.estimatedHourlyUsd ?? Infinity) || a.provider.localeCompare(b.provider));
|
|
166
|
+
}
|
|
167
|
+
return list;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Start a computer on the cheapest provider that offers this OS and has credentials (ties
|
|
171
|
+
* alphabetical), falling back down the list. An explicit `prefer` list keeps its order.
|
|
172
|
+
*/
|
|
48
173
|
export async function createComputer(providers, spec = {}, prefer) {
|
|
49
174
|
const os = spec.os ?? "linux";
|
|
50
|
-
const
|
|
51
|
-
|
|
175
|
+
const usable = planComputer(providers, os, prefer)
|
|
176
|
+
.filter((c) => !c.skip)
|
|
177
|
+
.map((c) => providers.find((p) => p.id === c.provider));
|
|
52
178
|
if (usable.length === 0) {
|
|
53
179
|
const offering = providers.filter((p) => p.computerOs.includes(os)).map((p) => p.id);
|
|
54
180
|
throw new RouterError(offering.length ? `no configured provider for a ${os} computer (offered by: ${offering.join(", ")})` : `no provider offers a ${os} computer`);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type CheckReport, type CheckStatus } from "./check.js";
|
|
2
|
+
import type { ProviderId } from "./types.js";
|
|
3
|
+
/** A `sandboxrouter check --save` file: one real run of every check step on one provider. */
|
|
4
|
+
export interface SavedCheck extends CheckReport {
|
|
5
|
+
tool: "sandboxrouter-check";
|
|
6
|
+
version: string;
|
|
7
|
+
date: string;
|
|
8
|
+
platform: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ConformanceRow {
|
|
11
|
+
provider: ProviderId;
|
|
12
|
+
name: string;
|
|
13
|
+
/** Null when nobody has saved a real run for this provider yet. */
|
|
14
|
+
run: null | {
|
|
15
|
+
date: string;
|
|
16
|
+
version: string;
|
|
17
|
+
passed: number;
|
|
18
|
+
/** Steps that ran and failed, with why. */
|
|
19
|
+
failed: {
|
|
20
|
+
step: string;
|
|
21
|
+
detail?: string;
|
|
22
|
+
}[];
|
|
23
|
+
/** Steps that don't apply (pause on adapters without it). */
|
|
24
|
+
skipped: string[];
|
|
25
|
+
/** Steps added to `check` after this run, so not covered by it. */
|
|
26
|
+
notRun: string[];
|
|
27
|
+
/** Steps that passed with a caveat, e.g. output only arrives at the end. */
|
|
28
|
+
notes: {
|
|
29
|
+
step: string;
|
|
30
|
+
detail?: string;
|
|
31
|
+
}[];
|
|
32
|
+
steps: Record<string, CheckStatus | "not run">;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One-word status of a provider's last real-account run, for tables: "passed" (every step that applies),
|
|
37
|
+
* "failing" (the last run had failures), or "untested" (only mock-tested; no real run saved yet).
|
|
38
|
+
*/
|
|
39
|
+
export declare function liveStatus(row: ConformanceRow): {
|
|
40
|
+
status: "passed" | "failing" | "untested";
|
|
41
|
+
label: string;
|
|
42
|
+
};
|
|
43
|
+
/** Saved check runs in `dir` (default results/conformance), keyed by provider. */
|
|
44
|
+
export declare function loadConformance(dir?: string): Map<ProviderId, SavedCheck>;
|
|
45
|
+
/** One row per sandbox provider in the catalog, scored against the current list of check steps. */
|
|
46
|
+
export declare function conformanceTable(saved?: Map<ProviderId, SavedCheck>): ConformanceRow[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CATALOG } from "./catalog.js";
|
|
4
|
+
import { CHECK_STEPS } from "./check.js";
|
|
5
|
+
/**
|
|
6
|
+
* One-word status of a provider's last real-account run, for tables: "passed" (every step that applies),
|
|
7
|
+
* "failing" (the last run had failures), or "untested" (only mock-tested; no real run saved yet).
|
|
8
|
+
*/
|
|
9
|
+
export function liveStatus(row) {
|
|
10
|
+
const r = row.run;
|
|
11
|
+
if (!r)
|
|
12
|
+
return { status: "untested", label: "not run yet" };
|
|
13
|
+
const day = r.date.slice(0, 10);
|
|
14
|
+
if (r.failed.length)
|
|
15
|
+
return { status: "failing", label: `failed ${r.failed.length} step${r.failed.length > 1 ? "s" : ""} (${day})` };
|
|
16
|
+
return { status: "passed", label: `passed (${day})` };
|
|
17
|
+
}
|
|
18
|
+
/** Saved check runs in `dir` (default results/conformance), keyed by provider. */
|
|
19
|
+
export function loadConformance(dir = "results/conformance") {
|
|
20
|
+
const out = new Map();
|
|
21
|
+
let files;
|
|
22
|
+
try {
|
|
23
|
+
files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
for (const f of files) {
|
|
29
|
+
try {
|
|
30
|
+
const r = JSON.parse(readFileSync(join(dir, f), "utf8"));
|
|
31
|
+
if (r?.tool === "sandboxrouter-check" && Array.isArray(r.results))
|
|
32
|
+
out.set(r.provider, r);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Not a check file.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** One row per sandbox provider in the catalog, scored against the current list of check steps. */
|
|
41
|
+
export function conformanceTable(saved = loadConformance()) {
|
|
42
|
+
return CATALOG.map((c) => {
|
|
43
|
+
const r = saved.get(c.id);
|
|
44
|
+
if (!r)
|
|
45
|
+
return { provider: c.id, name: c.name, run: null };
|
|
46
|
+
const by = new Map(r.results.map((x) => [x.step, x]));
|
|
47
|
+
const steps = Object.fromEntries(CHECK_STEPS.map((s) => [s, by.get(s)?.status ?? "not run"]));
|
|
48
|
+
const pick = (st) => r.results.filter((x) => x.status === st);
|
|
49
|
+
return {
|
|
50
|
+
provider: c.id,
|
|
51
|
+
name: c.name,
|
|
52
|
+
run: {
|
|
53
|
+
date: r.date,
|
|
54
|
+
version: r.version,
|
|
55
|
+
passed: pick("pass").length + pick("info").length,
|
|
56
|
+
failed: pick("fail").map((x) => ({ step: x.step, detail: x.detail })),
|
|
57
|
+
skipped: pick("skip").map((x) => x.step),
|
|
58
|
+
// A failed create ends the run early; that's a failure, not missing coverage.
|
|
59
|
+
notRun: by.get("create")?.status === "fail" ? [] : CHECK_STEPS.filter((s) => !by.has(s)),
|
|
60
|
+
notes: pick("info").map((x) => ({ step: x.step, detail: x.detail })),
|
|
61
|
+
steps,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
package/dist/cost.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Router } from "./router.js";
|
|
2
|
+
import type { ProviderId, SandboxSpec } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Real-cost check: does what a provider bills match what the catalog predicts?
|
|
5
|
+
*
|
|
6
|
+
* `costRun` starts one sandbox per provider at a known size, measures the size it really got
|
|
7
|
+
* (nproc, MemTotal), keeps every core busy for a fixed time (some providers bill active CPU only),
|
|
8
|
+
* kills it, and records the predicted cost from list price × wall time. `settle` later reads what
|
|
9
|
+
* the provider actually charged for that sandbox from its usage API, where it has one. Billing
|
|
10
|
+
* lags (Blaxel minutes, Daytona up to 48 h), so settle can be run again until every row has a bill.
|
|
11
|
+
*/
|
|
12
|
+
export interface Delivered {
|
|
13
|
+
vcpu: number;
|
|
14
|
+
memoryMb: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Bill {
|
|
17
|
+
/** What the provider says it charged for this sandbox, USD. Undefined when it can't be read. */
|
|
18
|
+
usd?: number;
|
|
19
|
+
/** Seconds the provider billed, when it reports them. */
|
|
20
|
+
seconds?: number;
|
|
21
|
+
/** Where the number came from, or why there is none. */
|
|
22
|
+
source: string;
|
|
23
|
+
at: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CostEntry {
|
|
26
|
+
provider: ProviderId;
|
|
27
|
+
sandboxId?: string;
|
|
28
|
+
requested: {
|
|
29
|
+
vcpu: number;
|
|
30
|
+
memoryGb: number;
|
|
31
|
+
};
|
|
32
|
+
delivered?: Delivered;
|
|
33
|
+
startedAt?: string;
|
|
34
|
+
endedAt?: string;
|
|
35
|
+
/** Wall time from the create call until kill returned. */
|
|
36
|
+
seconds?: number;
|
|
37
|
+
/** Catalog list price for the requested size. */
|
|
38
|
+
listUsdHour: number;
|
|
39
|
+
predictedUsd?: number;
|
|
40
|
+
/** Catalog list price for the size the sandbox really had. */
|
|
41
|
+
deliveredUsdHour?: number;
|
|
42
|
+
error?: string;
|
|
43
|
+
bill?: Bill;
|
|
44
|
+
}
|
|
45
|
+
export interface CostRun {
|
|
46
|
+
tool: "sandboxrouter-cost";
|
|
47
|
+
version: string;
|
|
48
|
+
date: string;
|
|
49
|
+
minutes: number;
|
|
50
|
+
load: boolean;
|
|
51
|
+
entries: CostEntry[];
|
|
52
|
+
}
|
|
53
|
+
export declare function costRun(opts: {
|
|
54
|
+
providers: ProviderId[];
|
|
55
|
+
minutes: number;
|
|
56
|
+
spec?: SandboxSpec;
|
|
57
|
+
load?: boolean;
|
|
58
|
+
router?: Router;
|
|
59
|
+
version: string;
|
|
60
|
+
onProgress?: (msg: string) => void;
|
|
61
|
+
}): Promise<CostRun>;
|
|
62
|
+
type Reader = (e: CostEntry) => Promise<Omit<Bill, "at">>;
|
|
63
|
+
/** Per-provider readers of the real charge for one sandbox. */
|
|
64
|
+
export declare const BILL_READERS: Partial<Record<ProviderId, Reader>>;
|
|
65
|
+
export declare function settle(run: CostRun): Promise<CostRun>;
|
|
66
|
+
export declare function costMarkdown(run: CostRun): string;
|
|
67
|
+
export {};
|
package/dist/cost.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { fittingSpec, hourlyCost } from "./catalog.js";
|
|
2
|
+
import { Router } from "./router.js";
|
|
3
|
+
const PROBE = `echo "$(nproc) $(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo)"`;
|
|
4
|
+
// One `yes` per core, detached so the exec returns; they die with the sandbox.
|
|
5
|
+
const LOAD = `nohup sh -c 'for i in $(seq $(nproc)); do yes > /dev/null & done; wait' > /dev/null 2>&1 &`;
|
|
6
|
+
const round = (n, d = 6) => Math.round(n * 10 ** d) / 10 ** d;
|
|
7
|
+
export async function costRun(opts) {
|
|
8
|
+
const router = opts.router ?? new Router();
|
|
9
|
+
const load = opts.load ?? true;
|
|
10
|
+
const log = opts.onProgress ?? (() => { });
|
|
11
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
|
+
const one = async (provider) => {
|
|
13
|
+
// Asked-for size, or else one the provider can take: its default size if that's fixed.
|
|
14
|
+
const sized = opts.spec?.vcpu !== undefined || opts.spec?.memoryGb !== undefined;
|
|
15
|
+
const { vcpu, memoryGb } = sized ? { vcpu: opts.spec?.vcpu ?? 2, memoryGb: opts.spec?.memoryGb ?? 4 } : fittingSpec(provider);
|
|
16
|
+
const entry = { provider, requested: { vcpu, memoryGb }, listUsdHour: hourlyCost(provider, { vcpu, memoryGb }) };
|
|
17
|
+
let box;
|
|
18
|
+
const t0 = Date.now();
|
|
19
|
+
try {
|
|
20
|
+
entry.startedAt = new Date(t0).toISOString();
|
|
21
|
+
box = await router.create({ vcpu, memoryGb, timeoutMinutes: opts.minutes + 10 }, { provider, fallback: false });
|
|
22
|
+
entry.sandboxId = box.nativeId;
|
|
23
|
+
const [cpus, mem] = (await box.exec(PROBE)).stdout.trim().split(/\s+/).map(Number);
|
|
24
|
+
if (cpus > 0 && mem > 0) {
|
|
25
|
+
entry.delivered = { vcpu: cpus, memoryMb: mem };
|
|
26
|
+
entry.deliveredUsdHour = round(hourlyCost(provider, { vcpu: cpus, memoryGb: mem / 1024 }));
|
|
27
|
+
}
|
|
28
|
+
if (load)
|
|
29
|
+
await box.exec(LOAD);
|
|
30
|
+
log(`${provider}: running ${box.nativeId} for ${opts.minutes} min`);
|
|
31
|
+
await sleep(opts.minutes * 60_000 - (Date.now() - t0));
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
entry.error = err.message.slice(0, 300);
|
|
35
|
+
log(`${provider}: ${entry.error}`);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
if (box) {
|
|
39
|
+
await box.kill().catch((e) => (entry.error = `kill failed: ${e.message.slice(0, 200)}`));
|
|
40
|
+
const t1 = Date.now();
|
|
41
|
+
entry.endedAt = new Date(t1).toISOString();
|
|
42
|
+
entry.seconds = Math.round((t1 - t0) / 1000);
|
|
43
|
+
entry.predictedUsd = round((entry.listUsdHour * entry.seconds) / 3600);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return entry;
|
|
47
|
+
};
|
|
48
|
+
const entries = await Promise.all(opts.providers.map(one));
|
|
49
|
+
return { tool: "sandboxrouter-cost", version: opts.version, date: new Date().toISOString(), minutes: opts.minutes, load, entries };
|
|
50
|
+
}
|
|
51
|
+
async function getJson(url, headers) {
|
|
52
|
+
const r = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) });
|
|
53
|
+
if (!r.ok)
|
|
54
|
+
throw new Error(`${r.status} ${(await r.text()).slice(0, 200)}`);
|
|
55
|
+
return r.json();
|
|
56
|
+
}
|
|
57
|
+
const hourFloor = (iso) => new Date(Math.floor(Date.parse(iso) / 3_600_000) * 3_600_000).toISOString();
|
|
58
|
+
const hourCeil = (iso) => new Date(Math.ceil(Date.parse(iso) / 3_600_000 + 1) * 3_600_000).toISOString();
|
|
59
|
+
/** Per-provider readers of the real charge for one sandbox. */
|
|
60
|
+
export const BILL_READERS = {
|
|
61
|
+
async daytona(e) {
|
|
62
|
+
const h = { Authorization: `Bearer ${process.env.DAYTONA_API_KEY}` };
|
|
63
|
+
const api = process.env.DAYTONA_API_URL ?? "https://app.daytona.io/api";
|
|
64
|
+
const { organizationId } = await getJson(`${api}/api-keys/current`, h);
|
|
65
|
+
const q = new URLSearchParams({ from: hourFloor(e.startedAt), to: hourCeil(e.endedAt) });
|
|
66
|
+
const rows = await getJson(`https://analytics.app.daytona.io/organization/${organizationId}/usage/sandbox?${q}`, h);
|
|
67
|
+
const row = rows.find((r) => r.sandboxId === e.sandboxId);
|
|
68
|
+
if (!row)
|
|
69
|
+
return { source: "daytona usage API: not posted yet (can take up to 48 h)" };
|
|
70
|
+
const seconds = row.firstStart && row.lastEnd ? Math.round((Date.parse(row.lastEnd) - Date.parse(row.firstStart)) / 1000) : undefined;
|
|
71
|
+
return { usd: round(row.totalPrice), seconds, source: "daytona analytics /usage/sandbox totalPrice" };
|
|
72
|
+
},
|
|
73
|
+
async blaxel(e) {
|
|
74
|
+
const h = { Authorization: `Bearer ${process.env.BL_API_KEY}` };
|
|
75
|
+
if (process.env.BL_WORKSPACE)
|
|
76
|
+
h["X-Blaxel-Workspace"] = process.env.BL_WORKSPACE;
|
|
77
|
+
const [acct] = await getJson("https://api.blaxel.ai/v0/accounts", h);
|
|
78
|
+
const q = new URLSearchParams({ startTime: hourFloor(e.startedAt), endTime: hourCeil(e.endedAt), groupBy: "resource_name" });
|
|
79
|
+
const j = await getJson(`https://api.blaxel.ai/v0/accounts/${acct.metadata.id}/metrics?${q}`, h);
|
|
80
|
+
const row = (j.data ?? []).find((d) => d.resourceName === e.sandboxId);
|
|
81
|
+
if (!row)
|
|
82
|
+
return { source: "blaxel metrics API: not posted yet (lags a few minutes)" };
|
|
83
|
+
return { usd: round(row.summary?.cost ?? 0), source: "blaxel /metrics cost (USD)" };
|
|
84
|
+
},
|
|
85
|
+
async maritime(e) {
|
|
86
|
+
const h = { Authorization: `Bearer ${process.env.MARITIME_API_KEY}` };
|
|
87
|
+
const api = process.env.MARITIME_API_URL ?? "https://api.maritime.sh";
|
|
88
|
+
const j = await getJson(`${api}/api/billing/metered-usage`, h);
|
|
89
|
+
const days = j.byMachineDay?.[`sandbox:${e.sandboxId}`] ?? [];
|
|
90
|
+
if (!days.length)
|
|
91
|
+
return { source: "maritime metered-usage: not posted yet" };
|
|
92
|
+
const cents = days.reduce((n, d) => n + (d.cents ?? 0), 0);
|
|
93
|
+
const seconds = days.reduce((n, d) => n + (d.seconds ?? 0), 0);
|
|
94
|
+
const model = j.plan ? `plan ${j.plan}` : "no usage plan on this account";
|
|
95
|
+
return { usd: round(cents / 100), seconds: Math.round(seconds), source: `maritime metered-usage (integer cents; ${model})` };
|
|
96
|
+
},
|
|
97
|
+
async sail(e) {
|
|
98
|
+
const h = { Authorization: `Bearer ${process.env.SAIL_API_KEY}` };
|
|
99
|
+
const api = (process.env.SAILBOX_API_URL ?? "https://sailbox-api.sailresearch.com").replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
100
|
+
const q = new URLSearchParams({ from: hourFloor(e.startedAt), to: hourCeil(e.endedAt), sailbox_id: e.sandboxId });
|
|
101
|
+
const j = await getJson(`${api}/v1/sailboxes/spend?${q}`, h);
|
|
102
|
+
const row = (j.sailboxes ?? []).find((s) => s.sailbox_id === e.sandboxId);
|
|
103
|
+
if (!row)
|
|
104
|
+
return { source: "sail /sailboxes/spend: not posted yet (a few minutes)" };
|
|
105
|
+
const fees = j.creation_fees_waived ? "creation fee waived" : `${row.creation_count ?? 0} creation charge`;
|
|
106
|
+
return {
|
|
107
|
+
usd: round((row.cost_usd_nanos ?? 0) / 1e9),
|
|
108
|
+
seconds: Math.round(row.running_seconds ?? 0),
|
|
109
|
+
source: `sail /sailboxes/spend cost_usd_nanos (estimate at list rates; ${fees}; ${Math.round(row.vcpu_seconds ?? 0)} vCPU-s, ${Math.round(row.memory_gib_seconds ?? 0)} GiB-s)`,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
async e2b() {
|
|
113
|
+
return { source: "no billing API: compare the console usage tab (console.e2b.dev, Usage)" };
|
|
114
|
+
},
|
|
115
|
+
async openscale() {
|
|
116
|
+
return { source: "no billing HTTP API: `openscale balance` before and after, or the dashboard" };
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
export async function settle(run) {
|
|
120
|
+
for (const e of run.entries) {
|
|
121
|
+
if (!e.sandboxId || !e.startedAt || !e.endedAt)
|
|
122
|
+
continue;
|
|
123
|
+
if (e.bill?.usd !== undefined)
|
|
124
|
+
continue;
|
|
125
|
+
const read = BILL_READERS[e.provider];
|
|
126
|
+
const at = new Date().toISOString();
|
|
127
|
+
if (!read) {
|
|
128
|
+
e.bill = { source: "no reader for this provider yet", at };
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
e.bill = { ...(await read(e)), at };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
e.bill = { source: `could not read: ${err.message.slice(0, 200)}`, at };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return run;
|
|
139
|
+
}
|
|
140
|
+
const usd = (n) => (n === undefined ? "n/a" : n < 0.01 ? `$${n.toFixed(5)}` : `$${n.toFixed(4)}`);
|
|
141
|
+
export function costMarkdown(run) {
|
|
142
|
+
const lines = [
|
|
143
|
+
"| Provider | Asked for | Got | Ran | Predicted (list × time) | Billed | Billed ÷ predicted | Source |",
|
|
144
|
+
"|---|---|---|---|---|---|---|---|",
|
|
145
|
+
];
|
|
146
|
+
for (const e of run.entries) {
|
|
147
|
+
const got = e.delivered ? `${e.delivered.vcpu} vCPU / ${(e.delivered.memoryMb / 1024).toFixed(1)} GB` : "n/a";
|
|
148
|
+
const ratio = e.bill?.usd !== undefined && e.predictedUsd ? `${((e.bill.usd / e.predictedUsd) * 100).toFixed(0)}%` : "n/a";
|
|
149
|
+
const ran = e.seconds !== undefined ? `${e.seconds} s${e.bill?.seconds !== undefined ? ` (billed ${e.bill.seconds} s)` : ""}` : "n/a";
|
|
150
|
+
lines.push(`| ${e.provider} | ${e.requested.vcpu} vCPU / ${e.requested.memoryGb} GB | ${got} | ${ran} | ${usd(e.predictedUsd)} | ${usd(e.bill?.usd)} | ${ratio} | ${e.error ? `error: ${e.error}` : (e.bill?.source ?? "not settled")} |`);
|
|
151
|
+
}
|
|
152
|
+
return lines.join("\n");
|
|
153
|
+
}
|
package/dist/gc.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LedgerEntry } from "./ledger.js";
|
|
2
|
+
import type { Router } from "./router.js";
|
|
3
|
+
export interface OpenSandbox extends LedgerEntry {
|
|
4
|
+
ageMinutes: number;
|
|
5
|
+
/** True once its requested lifetime has run out. */
|
|
6
|
+
expired: boolean;
|
|
7
|
+
/** List price times how long it has been up, capped at its lifetime (most providers stop it there). */
|
|
8
|
+
estimatedUsdSoFar: number;
|
|
9
|
+
}
|
|
10
|
+
export interface GcResult {
|
|
11
|
+
id: string;
|
|
12
|
+
action: "killed" | "gone" | "would kill" | "failed";
|
|
13
|
+
detail?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Every sandbox in the router's ledger that hasn't been seen killed. */
|
|
16
|
+
export declare function listOpen(router: Router, now?: number): OpenSandbox[];
|
|
17
|
+
/** Errors that mean the sandbox no longer exists, so there is nothing left to kill. */
|
|
18
|
+
export declare function isGone(err: unknown): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Kill leaked sandboxes from the ledger. By default only those past their lifetime; `all` kills
|
|
21
|
+
* every open one; `olderThanMinutes` kills those up at least that long. A sandbox that turns out
|
|
22
|
+
* to be gone already is just dropped from the ledger.
|
|
23
|
+
*/
|
|
24
|
+
export declare function gc(router: Router, opts?: {
|
|
25
|
+
all?: boolean;
|
|
26
|
+
olderThanMinutes?: number;
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
now?: number;
|
|
29
|
+
}): Promise<GcResult[]>;
|
package/dist/gc.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ProviderError } from "./types.js";
|
|
2
|
+
/** Every sandbox in the router's ledger that hasn't been seen killed. */
|
|
3
|
+
export function listOpen(router, now = Date.now()) {
|
|
4
|
+
return (router.ledger?.list() ?? []).map((e) => {
|
|
5
|
+
const created = Date.parse(e.createdAt);
|
|
6
|
+
const expires = Date.parse(e.expiresAt);
|
|
7
|
+
const upMs = Math.max(0, Math.min(now, expires) - created);
|
|
8
|
+
return {
|
|
9
|
+
...e,
|
|
10
|
+
ageMinutes: Math.round((now - created) / 60_000),
|
|
11
|
+
expired: now >= expires,
|
|
12
|
+
estimatedUsdSoFar: Math.round(e.usdHour * (upMs / 3_600_000) * 10_000) / 10_000,
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** Errors that mean the sandbox no longer exists, so there is nothing left to kill. */
|
|
17
|
+
export function isGone(err) {
|
|
18
|
+
if (err instanceof ProviderError && (err.status === 404 || err.status === 410))
|
|
19
|
+
return true;
|
|
20
|
+
return /\b(404|410)\b|not found|no such|does not exist|doesn't exist|not exist|unknown sandbox|already (deleted|terminated|stopped)|\bgone\b/i.test(err?.message ?? String(err));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Kill leaked sandboxes from the ledger. By default only those past their lifetime; `all` kills
|
|
24
|
+
* every open one; `olderThanMinutes` kills those up at least that long. A sandbox that turns out
|
|
25
|
+
* to be gone already is just dropped from the ledger.
|
|
26
|
+
*/
|
|
27
|
+
export async function gc(router, opts = {}) {
|
|
28
|
+
const open = listOpen(router, opts.now);
|
|
29
|
+
const pick = open.filter((s) => opts.all || s.expired || (opts.olderThanMinutes !== undefined && s.ageMinutes >= opts.olderThanMinutes));
|
|
30
|
+
const results = [];
|
|
31
|
+
for (const s of pick) {
|
|
32
|
+
if (opts.dryRun) {
|
|
33
|
+
results.push({ id: s.id, action: "would kill", detail: s.expired ? "past its lifetime" : `up ${s.ageMinutes} min` });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const box = await router.get(s.id);
|
|
38
|
+
await box.kill(); // closes the ledger entry
|
|
39
|
+
results.push({ id: s.id, action: "killed" });
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (isGone(err)) {
|
|
43
|
+
router.ledger?.close(s.id, "gone");
|
|
44
|
+
results.push({ id: s.id, action: "gone", detail: err.message });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
results.push({ id: s.id, action: "failed", detail: err?.message ?? String(err) });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!opts.dryRun && results.length)
|
|
52
|
+
router.ledger?.compact();
|
|
53
|
+
return results;
|
|
54
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { checkProvider, CHECK_STEPS, type CheckReport, type CheckResult, type CheckStatus } from "./check.js";
|
|
2
|
+
export { Ledger, defaultLedgerFile, type LedgerEntry } from "./ledger.js";
|
|
3
|
+
export { conformanceTable, loadConformance, type ConformanceRow, type SavedCheck } from "./conformance.js";
|
|
4
|
+
export { gc, listOpen, isGone, type GcResult, type OpenSandbox } from "./gc.js";
|
|
5
|
+
export { Router, RoutedSandbox, defaultProviders, defaultComputerProviders, parseId, type Candidate, type RouterOptions } from "./router.js";
|
|
2
6
|
export { CATALOG, hourlyCost, unfit, info, type ProviderInfo } from "./catalog.js";
|
|
3
7
|
export { createApp } from "./server.js";
|
|
4
|
-
export { RoutedComputer, createComputer, type ActionResult, type ComputerAction, type ComputerActionName, type ComputerInfo, type ComputerOs, type ComputerProvider, type ComputerSpec, type ProviderComputer, } from "./computers.js";
|
|
8
|
+
export { RoutedComputer, createComputer, planComputer, computerCatalog, computerHourly, COMPUTER_ENTRIES, type ComputerCatalogEntry, type ComputerCandidate, type ComputerProviderId, type ActionResult, type ComputerAction, type ComputerActionName, type ComputerInfo, type ComputerOs, type ComputerProvider, type ComputerSpec, type ProviderComputer, } from "./computers.js";
|
|
5
9
|
export { bench, benchMarkdown, type BenchRow } from "./bench.js";
|
|
6
10
|
export { aggregate, loadReports, makeReport, renderLeaderboard, type BenchReport, type LeaderRow } from "./leaderboard.js";
|
|
7
11
|
export { MaritimeProvider } from "./providers/maritime.js";
|
|
@@ -20,4 +24,26 @@ export { CloudflareProvider } from "./providers/cloudflare.js";
|
|
|
20
24
|
export { NorthflankProvider } from "./providers/northflank.js";
|
|
21
25
|
export { BoatProvider } from "./providers/boat.js";
|
|
22
26
|
export { ExeDevProvider } from "./providers/exedev.js";
|
|
27
|
+
export { AgentCoreProvider } from "./providers/agentcore.js";
|
|
28
|
+
export { AzureSessionsProvider } from "./providers/azure.js";
|
|
29
|
+
export { LambdaMicrovmProvider, writeLambdaMicrovmBundle } from "./providers/lambda.js";
|
|
30
|
+
export { AlibabaProvider } from "./providers/alibaba.js";
|
|
31
|
+
export { BeamProvider } from "./providers/beam.js";
|
|
32
|
+
export { CodeSandboxProvider } from "./providers/codesandbox.js";
|
|
33
|
+
export { DenoProvider } from "./providers/deno.js";
|
|
34
|
+
export { FreestyleProvider } from "./providers/freestyle.js";
|
|
35
|
+
export { KoyebProvider } from "./providers/koyeb.js";
|
|
36
|
+
export { DedalusProvider } from "./providers/dedalus.js";
|
|
37
|
+
export { OpenScaleProvider } from "./providers/openscale.js";
|
|
38
|
+
export { SuperserveProvider } from "./providers/superserve.js";
|
|
39
|
+
export { SailProvider } from "./providers/sail.js";
|
|
40
|
+
export { InstaboxProvider, INSTABOX_SIZES, instaboxSize } from "./providers/instabox.js";
|
|
41
|
+
export { Agent37Provider, AGENT37_SHAPES, agent37Shape } from "./providers/agent37.js";
|
|
42
|
+
export { NovitaProvider } from "./providers/novita.js";
|
|
43
|
+
export { LangSmithProvider, langsmithSize } from "./providers/langsmith.js";
|
|
44
|
+
export { UpstashProvider } from "./providers/upstash.js";
|
|
45
|
+
export { OrgoProvider } from "./providers/orgo.js";
|
|
46
|
+
export { MobilerunProvider } from "./providers/mobilerun.js";
|
|
47
|
+
export { LimrunProvider } from "./providers/limrun.js";
|
|
48
|
+
export { WorkspacesAgentProvider } from "./providers/workspaces.js";
|
|
23
49
|
export * from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { checkProvider, CHECK_STEPS } from "./check.js";
|
|
2
|
+
export { Ledger, defaultLedgerFile } from "./ledger.js";
|
|
3
|
+
export { conformanceTable, loadConformance } from "./conformance.js";
|
|
4
|
+
export { gc, listOpen, isGone } from "./gc.js";
|
|
5
|
+
export { Router, RoutedSandbox, defaultProviders, defaultComputerProviders, parseId } from "./router.js";
|
|
2
6
|
export { CATALOG, hourlyCost, unfit, info } from "./catalog.js";
|
|
3
7
|
export { createApp } from "./server.js";
|
|
4
|
-
export { RoutedComputer, createComputer, } from "./computers.js";
|
|
8
|
+
export { RoutedComputer, createComputer, planComputer, computerCatalog, computerHourly, COMPUTER_ENTRIES, } from "./computers.js";
|
|
5
9
|
export { bench, benchMarkdown } from "./bench.js";
|
|
6
10
|
export { aggregate, loadReports, makeReport, renderLeaderboard } from "./leaderboard.js";
|
|
7
11
|
export { MaritimeProvider } from "./providers/maritime.js";
|
|
@@ -20,4 +24,26 @@ export { CloudflareProvider } from "./providers/cloudflare.js";
|
|
|
20
24
|
export { NorthflankProvider } from "./providers/northflank.js";
|
|
21
25
|
export { BoatProvider } from "./providers/boat.js";
|
|
22
26
|
export { ExeDevProvider } from "./providers/exedev.js";
|
|
27
|
+
export { AgentCoreProvider } from "./providers/agentcore.js";
|
|
28
|
+
export { AzureSessionsProvider } from "./providers/azure.js";
|
|
29
|
+
export { LambdaMicrovmProvider, writeLambdaMicrovmBundle } from "./providers/lambda.js";
|
|
30
|
+
export { AlibabaProvider } from "./providers/alibaba.js";
|
|
31
|
+
export { BeamProvider } from "./providers/beam.js";
|
|
32
|
+
export { CodeSandboxProvider } from "./providers/codesandbox.js";
|
|
33
|
+
export { DenoProvider } from "./providers/deno.js";
|
|
34
|
+
export { FreestyleProvider } from "./providers/freestyle.js";
|
|
35
|
+
export { KoyebProvider } from "./providers/koyeb.js";
|
|
36
|
+
export { DedalusProvider } from "./providers/dedalus.js";
|
|
37
|
+
export { OpenScaleProvider } from "./providers/openscale.js";
|
|
38
|
+
export { SuperserveProvider } from "./providers/superserve.js";
|
|
39
|
+
export { SailProvider } from "./providers/sail.js";
|
|
40
|
+
export { InstaboxProvider, INSTABOX_SIZES, instaboxSize } from "./providers/instabox.js";
|
|
41
|
+
export { Agent37Provider, AGENT37_SHAPES, agent37Shape } from "./providers/agent37.js";
|
|
42
|
+
export { NovitaProvider } from "./providers/novita.js";
|
|
43
|
+
export { LangSmithProvider, langsmithSize } from "./providers/langsmith.js";
|
|
44
|
+
export { UpstashProvider } from "./providers/upstash.js";
|
|
45
|
+
export { OrgoProvider } from "./providers/orgo.js";
|
|
46
|
+
export { MobilerunProvider } from "./providers/mobilerun.js";
|
|
47
|
+
export { LimrunProvider } from "./providers/limrun.js";
|
|
48
|
+
export { WorkspacesAgentProvider } from "./providers/workspaces.js";
|
|
23
49
|
export * from "./types.js";
|