@volter/browser-sandboxes 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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/bin/control-plane.d.ts +3 -0
- package/dist/bin/control-plane.d.ts.map +1 -0
- package/dist/bin/control-plane.js +11 -0
- package/dist/bin/control-plane.js.map +1 -0
- package/dist/bin/mcp.d.ts +3 -0
- package/dist/bin/mcp.d.ts.map +1 -0
- package/dist/bin/mcp.js +10 -0
- package/dist/bin/mcp.js.map +1 -0
- package/dist/client.d.ts +156 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +225 -0
- package/dist/client.js.map +1 -0
- package/dist/contract.d.ts +13 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +79 -0
- package/dist/contract.js.map +1 -0
- package/dist/control-plane.d.ts +14 -0
- package/dist/control-plane.d.ts.map +1 -0
- package/dist/control-plane.js +133 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/docker-provider.d.ts +23 -0
- package/dist/docker-provider.d.ts.map +1 -0
- package/dist/docker-provider.js +50 -0
- package/dist/docker-provider.js.map +1 -0
- package/dist/fly-provider.d.ts +51 -0
- package/dist/fly-provider.d.ts.map +1 -0
- package/dist/fly-provider.js +135 -0
- package/dist/fly-provider.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +90 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +113 -0
- package/dist/manager.js.map +1 -0
- package/dist/mcp.d.ts +12 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +142 -0
- package/dist/mcp.js.map +1 -0
- package/dist/providers.d.ts +8 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +17 -0
- package/dist/providers.js.map +1 -0
- package/package.json +45 -0
package/dist/contract.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Walks the hosted wire contract against a live server: negotiates the
|
|
2
|
+
// descriptor, checks the catalogue is covered, calls every safe route, and
|
|
3
|
+
// confirms refusals for an unknown route, a route outside the base path, a
|
|
4
|
+
// missing token, and a client stating another version. Returns failures; the
|
|
5
|
+
// caller decides what to do with them. The same walk proves a self-hosted
|
|
6
|
+
// tier and a managed service are one API.
|
|
7
|
+
import { SUBSTRATE_AGENT_PROTOCOL, SUBSTRATE_AGENT_ROUTES, SUBSTRATE_CONTROL_PLANE_ROUTES, SUBSTRATE_PROTOCOL_HEADER, hostedProtocolHeader, missingHostedRoutes, negotiateHostedProtocol, parseHostedProtocolHeader, } from "@volter/browser-runtime/browser-hosted-protocol.js";
|
|
8
|
+
export async function runHostedContract({ baseUrl, token, protocol, fetch: network = globalThis.fetch }) {
|
|
9
|
+
const failures = [];
|
|
10
|
+
const check = (label, ok, detail) => { if (!ok)
|
|
11
|
+
failures.push({ label, ...(detail !== undefined ? { detail } : {}) }); };
|
|
12
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
13
|
+
const auth = token ? { authorization: `Bearer ${token}` } : {};
|
|
14
|
+
const call = (path, init = {}) => {
|
|
15
|
+
const headers = new Headers(init.headers);
|
|
16
|
+
for (const [name, value] of Object.entries(auth))
|
|
17
|
+
headers.set(name, value);
|
|
18
|
+
return network(`${base}${path}`, { ...init, headers });
|
|
19
|
+
};
|
|
20
|
+
const catalogue = protocol === SUBSTRATE_AGENT_PROTOCOL ? SUBSTRATE_AGENT_ROUTES : SUBSTRATE_CONTROL_PLANE_ROUTES;
|
|
21
|
+
// 1. The descriptor, negotiated before anything else is trusted.
|
|
22
|
+
const root = await call("/");
|
|
23
|
+
check(`${protocol}: GET / answers`, root.ok, root.status);
|
|
24
|
+
const descriptor = root.ok ? await root.json().catch(() => undefined) : undefined;
|
|
25
|
+
const negotiated = negotiateHostedProtocol(descriptor, { protocol });
|
|
26
|
+
check(`${protocol}: descriptor negotiates`, negotiated.ok, negotiated);
|
|
27
|
+
const sent = parseHostedProtocolHeader(root.headers.get(SUBSTRATE_PROTOCOL_HEADER));
|
|
28
|
+
check(`${protocol}: every response carries ${SUBSTRATE_PROTOCOL_HEADER}`, sent?.protocol === protocol && sent.version === 1, root.headers.get(SUBSTRATE_PROTOCOL_HEADER));
|
|
29
|
+
if (!negotiated.ok)
|
|
30
|
+
return failures;
|
|
31
|
+
const basePath = negotiated.descriptor.basePath.replace(/\/$/, "");
|
|
32
|
+
const versioned = (path, init) => call(`${basePath}${path}`, init);
|
|
33
|
+
// 2. The catalogue the server claims covers the one the client speaks.
|
|
34
|
+
const missing = missingHostedRoutes(negotiated.descriptor.routes, catalogue);
|
|
35
|
+
check(`${protocol}: the server's catalogue covers the client's`, missing.length === 0, missing.map((r) => `${r.method} ${r.path}`));
|
|
36
|
+
// 3. Every safe route answers something other than "no such route".
|
|
37
|
+
for (const route of catalogue.filter((r) => r.safe && !r.path.includes("{"))) {
|
|
38
|
+
if (route.path === "/events") {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
try {
|
|
41
|
+
const response = await versioned(route.path, { signal: controller.signal });
|
|
42
|
+
check(`${protocol}: ${route.method} ${route.path} streams events`, response.ok && /text\/event-stream/.test(response.headers.get("content-type") ?? ""), response.status);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
check(`${protocol}: ${route.method} ${route.path} streams events`, false, String(error));
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
controller.abort();
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const query = route.path.startsWith("/fs/") ? "?path=/workspace&root=/workspace" : "";
|
|
53
|
+
const response = await versioned(`${route.path}${query}`, { method: route.method });
|
|
54
|
+
// A route may answer 404 for a missing resource; only "no such route" means it is not served.
|
|
55
|
+
const body = response.status === 404 ? (await response.json().catch(() => ({}))) : undefined;
|
|
56
|
+
const routed = response.status !== 404 || !/no such route/.test(String(body?.error ?? ""));
|
|
57
|
+
check(`${protocol}: ${route.method} ${route.path} is routed`, routed, { status: response.status, ...(body ? { body } : {}) });
|
|
58
|
+
check(`${protocol}: ${route.method} ${route.path} carries the protocol header`, response.headers.get(SUBSTRATE_PROTOCOL_HEADER) === hostedProtocolHeader(protocol), response.headers.get(SUBSTRATE_PROTOCOL_HEADER));
|
|
59
|
+
if (!body)
|
|
60
|
+
await response.arrayBuffer().catch(() => undefined);
|
|
61
|
+
}
|
|
62
|
+
// 4. Refusals are refusals.
|
|
63
|
+
const unknown = await versioned("/definitely-not-a-route");
|
|
64
|
+
const unknownBody = (await unknown.json().catch(() => ({})));
|
|
65
|
+
check(`${protocol}: an unknown route is 404 with a JSON error`, unknown.status === 404 && typeof unknownBody.error === "string", unknown.status);
|
|
66
|
+
const unversioned = await call("/health");
|
|
67
|
+
check(`${protocol}: a route outside the base path is 404`, unversioned.status === 404, unversioned.status);
|
|
68
|
+
await unversioned.arrayBuffer().catch(() => undefined);
|
|
69
|
+
if (token) {
|
|
70
|
+
const anonymous = await network(`${base}${basePath}/health`);
|
|
71
|
+
check(`${protocol}: a missing token is 401`, anonymous.status === 401, anonymous.status);
|
|
72
|
+
await anonymous.arrayBuffer().catch(() => undefined);
|
|
73
|
+
}
|
|
74
|
+
const wrongVersion = await versioned("/health", { headers: { [SUBSTRATE_PROTOCOL_HEADER]: `${protocol}/999` } });
|
|
75
|
+
check(`${protocol}: a client stating another version is refused with 426`, wrongVersion.status === 426, wrongVersion.status);
|
|
76
|
+
await wrongVersion.arrayBuffer().catch(() => undefined);
|
|
77
|
+
return failures;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as http from "node:http";
|
|
2
|
+
import type { SandboxManager } from "./manager.js";
|
|
3
|
+
export interface ControlPlaneOptions {
|
|
4
|
+
manager: SandboxManager;
|
|
5
|
+
port?: number;
|
|
6
|
+
bind?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ControlPlaneServer {
|
|
9
|
+
server: http.Server;
|
|
10
|
+
port: number;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function serveControlPlane({ manager, port, bind }: ControlPlaneOptions): Promise<ControlPlaneServer>;
|
|
14
|
+
//# sourceMappingURL=control-plane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAKlC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAWD,wBAAsB,iBAAiB,CAAC,EAAE,OAAO,EAAE,IAAW,EAAE,IAAkB,EAAE,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiGtI"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// A control plane a page can call: creates sandboxes through the manager and
|
|
2
|
+
// proxies each sandbox's agent under /sandboxes/<id>/agent/, adding the agent
|
|
3
|
+
// token server-side so the page never holds it, plus the CORS and
|
|
4
|
+
// embedder-policy headers a cross-origin-isolated page needs to frame a guest.
|
|
5
|
+
import * as http from "node:http";
|
|
6
|
+
import { SUBSTRATE_CONTROL_PLANE_PROTOCOL, SUBSTRATE_PROTOCOL_BASE_PATH, SUBSTRATE_PROTOCOL_HEADER, SUBSTRATE_PROTOCOL_VERSION, describeControlPlaneProtocol, hostedProtocolHeader, parseHostedProtocolHeader, } from "@volter/browser-runtime/browser-hosted-protocol.js";
|
|
7
|
+
const HOP_HEADERS = new Set(["content-length", "transfer-encoding", "connection", "keep-alive"]);
|
|
8
|
+
const REQUEST_HEADERS_DROPPED = new Set(["host", "authorization", "connection", "content-length", "origin"]);
|
|
9
|
+
const PROTOCOL = hostedProtocolHeader(SUBSTRATE_CONTROL_PLANE_PROTOCOL);
|
|
10
|
+
function versionedPath(pathname) {
|
|
11
|
+
if (pathname === SUBSTRATE_PROTOCOL_BASE_PATH)
|
|
12
|
+
return "/";
|
|
13
|
+
return pathname.startsWith(`${SUBSTRATE_PROTOCOL_BASE_PATH}/`) ? pathname.slice(SUBSTRATE_PROTOCOL_BASE_PATH.length) : undefined;
|
|
14
|
+
}
|
|
15
|
+
export async function serveControlPlane({ manager, port = 4291, bind = "127.0.0.1" }) {
|
|
16
|
+
const cors = (request) => ({
|
|
17
|
+
"access-control-allow-origin": request.headers.origin ?? "*",
|
|
18
|
+
"access-control-allow-methods": "GET,POST,PUT,DELETE,OPTIONS",
|
|
19
|
+
"access-control-allow-headers": "authorization,content-type",
|
|
20
|
+
"cross-origin-embedder-policy": "credentialless",
|
|
21
|
+
"cross-origin-resource-policy": "cross-origin",
|
|
22
|
+
});
|
|
23
|
+
const json = (request, response, status, value) => {
|
|
24
|
+
response.writeHead(status, { ...cors(request), "content-type": "application/json" });
|
|
25
|
+
response.end(JSON.stringify(value));
|
|
26
|
+
};
|
|
27
|
+
const readBody = (request) => new Promise((done, fail) => {
|
|
28
|
+
const chunks = [];
|
|
29
|
+
request.on("data", (chunk) => chunks.push(chunk));
|
|
30
|
+
request.on("end", () => done(Buffer.concat(chunks)));
|
|
31
|
+
request.on("error", fail);
|
|
32
|
+
});
|
|
33
|
+
const server = http.createServer(async (request, response) => {
|
|
34
|
+
response.setHeader(SUBSTRATE_PROTOCOL_HEADER, PROTOCOL);
|
|
35
|
+
const url = new URL(request.url ?? "/", "http://control-plane.invalid");
|
|
36
|
+
const method = request.method ?? "GET";
|
|
37
|
+
try {
|
|
38
|
+
if (method === "OPTIONS") {
|
|
39
|
+
response.writeHead(204, cors(request));
|
|
40
|
+
response.end();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (method === "GET" && url.pathname === "/")
|
|
44
|
+
return json(request, response, 200, describeControlPlaneProtocol());
|
|
45
|
+
const pathname = versionedPath(url.pathname);
|
|
46
|
+
if (pathname === undefined)
|
|
47
|
+
return json(request, response, 404, { error: `no such route; the catalogue is under ${SUBSTRATE_PROTOCOL_BASE_PATH}` });
|
|
48
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
49
|
+
// A stated protocol is judged by the server it is addressed to: this one
|
|
50
|
+
// for its own routes, the agent for everything under its proxy prefix.
|
|
51
|
+
const proxying = segments[0] === "sandboxes" && segments[2] === "agent";
|
|
52
|
+
const stated = request.headers[SUBSTRATE_PROTOCOL_HEADER];
|
|
53
|
+
if (!proxying && typeof stated === "string") {
|
|
54
|
+
const parsed = parseHostedProtocolHeader(stated);
|
|
55
|
+
if (!parsed || parsed.protocol !== SUBSTRATE_CONTROL_PLANE_PROTOCOL || parsed.version !== SUBSTRATE_PROTOCOL_VERSION) {
|
|
56
|
+
return json(request, response, 426, { error: `this server speaks ${PROTOCOL}, the client stated ${stated}`, protocol: PROTOCOL });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (pathname === "/health")
|
|
60
|
+
return json(request, response, 200, { ok: true, sandboxes: manager.list().length });
|
|
61
|
+
if (segments[0] !== "sandboxes")
|
|
62
|
+
return json(request, response, 404, { error: "no such route" });
|
|
63
|
+
if (segments.length === 1 && method === "GET")
|
|
64
|
+
return json(request, response, 200, manager.list());
|
|
65
|
+
if (segments.length === 1 && method === "POST") {
|
|
66
|
+
const text = (await readBody(request)).toString("utf8");
|
|
67
|
+
const options = (text ? JSON.parse(text) : {});
|
|
68
|
+
const created = await manager.create({ ...(options.volume ? { volume: options.volume } : {}) });
|
|
69
|
+
return json(request, response, 201, { id: created.id, agent: `${SUBSTRATE_PROTOCOL_BASE_PATH}/sandboxes/${created.id}/agent`, state: created.state });
|
|
70
|
+
}
|
|
71
|
+
const id = segments[1] ?? "";
|
|
72
|
+
const sandbox = manager.get(id);
|
|
73
|
+
if (!sandbox)
|
|
74
|
+
return json(request, response, 404, { error: "unknown sandbox" });
|
|
75
|
+
if (segments.length === 2 && method === "DELETE") {
|
|
76
|
+
await sandbox.kill();
|
|
77
|
+
return json(request, response, 200, { killed: sandbox.id });
|
|
78
|
+
}
|
|
79
|
+
if (segments[2] === "agent") {
|
|
80
|
+
const target = `${sandbox.url}/${segments.slice(3).join("/")}${url.search}`;
|
|
81
|
+
const headers = { authorization: `Bearer ${sandbox.token}` };
|
|
82
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
83
|
+
if (typeof value === "string" && !REQUEST_HEADERS_DROPPED.has(name))
|
|
84
|
+
headers[name] = value;
|
|
85
|
+
}
|
|
86
|
+
const body = method === "GET" || method === "HEAD" ? undefined : await readBody(request);
|
|
87
|
+
// A page that navigates away closes its event stream; abort upstream instead of erroring here.
|
|
88
|
+
const abort = new AbortController();
|
|
89
|
+
request.on("close", () => abort.abort());
|
|
90
|
+
const proxied = await fetch(target, {
|
|
91
|
+
method, headers, redirect: "manual", signal: abort.signal,
|
|
92
|
+
...(body ? { body: body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) } : {}),
|
|
93
|
+
});
|
|
94
|
+
const responseHeaders = { ...cors(request) };
|
|
95
|
+
proxied.headers.forEach((value, name) => { if (!HOP_HEADERS.has(name))
|
|
96
|
+
responseHeaders[name] = value; });
|
|
97
|
+
// A proxied response is the agent's; it carries the agent's protocol, not this server's.
|
|
98
|
+
response.removeHeader(SUBSTRATE_PROTOCOL_HEADER);
|
|
99
|
+
response.writeHead(proxied.status, responseHeaders);
|
|
100
|
+
try {
|
|
101
|
+
if (proxied.body) {
|
|
102
|
+
const reader = proxied.body.getReader();
|
|
103
|
+
for (;;) {
|
|
104
|
+
const { value, done } = await reader.read();
|
|
105
|
+
if (done)
|
|
106
|
+
break;
|
|
107
|
+
response.write(Buffer.from(value));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch { /* client or upstream went away */ }
|
|
112
|
+
response.end();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
return json(request, response, 404, { error: "no such route" });
|
|
116
|
+
}
|
|
117
|
+
catch (cause) {
|
|
118
|
+
if (response.headersSent) {
|
|
119
|
+
response.destroy();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
return json(request, response, 500, { error: cause instanceof Error ? cause.message : String(cause) });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
await new Promise((resolve) => server.listen(port, bind, () => resolve()));
|
|
126
|
+
const address = server.address();
|
|
127
|
+
return {
|
|
128
|
+
server,
|
|
129
|
+
port: typeof address === "object" && address ? address.port : port,
|
|
130
|
+
close: () => new Promise((resolve) => server.close(() => resolve())),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=control-plane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-plane.js","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ProviderStartSpec, ProviderStarted, SandboxProvider } from "./manager.js";
|
|
2
|
+
export interface DockerProviderOptions {
|
|
3
|
+
/** The guest image carrying the agent; the default is the image `build-image` produces. */
|
|
4
|
+
image?: string;
|
|
5
|
+
/** Bind this repository at /repo and run the agent from its built package instead of the image's. */
|
|
6
|
+
repo?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Runs the agent image as a local container with the guest root at "/". */
|
|
10
|
+
export declare class DockerProvider implements SandboxProvider {
|
|
11
|
+
readonly image: string;
|
|
12
|
+
readonly repo: string | undefined;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
constructor({ image, repo, label }?: DockerProviderOptions);
|
|
15
|
+
private docker;
|
|
16
|
+
start({ id, token, image, limits, volume }: ProviderStartSpec): ProviderStarted;
|
|
17
|
+
pause({ name }: ProviderStarted): void;
|
|
18
|
+
resume({ name }: ProviderStarted): void;
|
|
19
|
+
stop({ name }: ProviderStarted): void;
|
|
20
|
+
running(): Set<string>;
|
|
21
|
+
removeVolume(volume: string): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=docker-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-provider.d.ts","sourceRoot":"","sources":["../src/docker-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExF,MAAM,WAAW,qBAAqB;IACpC,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4EAA4E;AAC5E,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,EAAE,KAAmC,EAAE,IAAI,EAAE,KAAkC,EAAE,GAAE,qBAA0B;IAOzH,OAAO,CAAC,MAAM;IAId,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAkB,EAAE,MAAW,EAAE,MAAM,EAAE,EAAE,iBAAiB,GAAG,eAAe;IAmBjG,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,IAAI;IACtC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,IAAI;IACvC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,IAAI;IACrC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC;IAItB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CACnC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
/** Runs the agent image as a local container with the guest root at "/". */
|
|
3
|
+
export class DockerProvider {
|
|
4
|
+
image;
|
|
5
|
+
repo;
|
|
6
|
+
label;
|
|
7
|
+
constructor({ image = "substrate-node-host:local", repo, label = "volter.substrate.sandbox" } = {}) {
|
|
8
|
+
// With the repository bound in, the base Node image suffices; the agent runs from the built package.
|
|
9
|
+
this.image = image === "substrate-node-host:local" && repo ? "node:22-bookworm-slim" : image;
|
|
10
|
+
this.repo = repo;
|
|
11
|
+
this.label = label;
|
|
12
|
+
}
|
|
13
|
+
docker(args) {
|
|
14
|
+
return execFileSync("docker", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
15
|
+
}
|
|
16
|
+
start({ id, token, image = this.image, limits = {}, volume }) {
|
|
17
|
+
const name = `substrate-sandbox-${id.slice(0, 12)}`;
|
|
18
|
+
this.docker([
|
|
19
|
+
"run", "-d", "--rm", "--name", name, "--label", `${this.label}=${id}`,
|
|
20
|
+
"-p", "127.0.0.1::8787",
|
|
21
|
+
// Resource limits are the provider's job; the manager only passes them through.
|
|
22
|
+
...(limits.cpus ? ["--cpus", String(limits.cpus)] : []),
|
|
23
|
+
...(limits.memoryBytes ? ["--memory", String(limits.memoryBytes)] : []),
|
|
24
|
+
...(limits.pids ? ["--pids-limit", String(limits.pids)] : []),
|
|
25
|
+
// A named volume is the workspace that outlives the sandbox.
|
|
26
|
+
...(volume ? ["-v", `${volume}:/workspace`] : []),
|
|
27
|
+
...(this.repo ? ["-v", `${this.repo}:/repo:ro`, "-w", "/repo"] : []),
|
|
28
|
+
"-e", "SUBSTRATE_ROOT=/", "-e", `SUBSTRATE_TOKEN=${token}`, "-e", "PORT=8787", "-e", "HOST=::",
|
|
29
|
+
image,
|
|
30
|
+
...(this.repo ? ["node", "/repo/packages/host-node/dist/bin/agent.js"] : []),
|
|
31
|
+
]);
|
|
32
|
+
const published = this.docker(["port", name, "8787/tcp"]).split("\n")[0] ?? "";
|
|
33
|
+
return { name, url: `http://127.0.0.1:${published.slice(published.lastIndexOf(":") + 1)}` };
|
|
34
|
+
}
|
|
35
|
+
pause({ name }) { this.docker(["pause", name]); }
|
|
36
|
+
resume({ name }) { this.docker(["unpause", name]); }
|
|
37
|
+
stop({ name }) { try {
|
|
38
|
+
this.docker(["rm", "-f", name]);
|
|
39
|
+
}
|
|
40
|
+
catch { /* already gone */ } }
|
|
41
|
+
running() {
|
|
42
|
+
const out = this.docker(["ps", "--filter", `label=${this.label}`, "--format", `{{.Label "${this.label}"}}`]);
|
|
43
|
+
return new Set(out ? out.split("\n") : []);
|
|
44
|
+
}
|
|
45
|
+
removeVolume(volume) { try {
|
|
46
|
+
this.docker(["volume", "rm", volume]);
|
|
47
|
+
}
|
|
48
|
+
catch { /* absent */ } }
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=docker-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-provider.js","sourceRoot":"","sources":["../src/docker-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ProviderStartSpec, ProviderStarted, SandboxProvider } from "./manager.js";
|
|
2
|
+
export interface FlyProviderOptions {
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
app?: string;
|
|
6
|
+
org?: string;
|
|
7
|
+
region?: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
/**
|
|
11
|
+
* How machines are reached. The twin publishes loopback ports; Fly itself
|
|
12
|
+
* exposes machines on their private IPv6 network, reached through flyctl's
|
|
13
|
+
* WireGuard proxy using the existing login ("proxy"), or directly when the
|
|
14
|
+
* caller is already on that network ("direct").
|
|
15
|
+
*/
|
|
16
|
+
access?: "proxy" | "direct";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fly Machines provider: the same four calls against api.machines.dev. Vendor
|
|
20
|
+
* truth carried through: a stopped machine restarts from a clean rootfs, so
|
|
21
|
+
* only a volume-mounted workspace survives pause and resume; guest memory is
|
|
22
|
+
* sized in 256 MiB steps; a deleted volume lingers as pending_destroy.
|
|
23
|
+
*/
|
|
24
|
+
export declare class FlyProvider implements SandboxProvider {
|
|
25
|
+
readonly apiUrl: string;
|
|
26
|
+
readonly app: string;
|
|
27
|
+
readonly org: string;
|
|
28
|
+
readonly region: string;
|
|
29
|
+
readonly image: string;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
readonly access: "proxy" | "direct";
|
|
32
|
+
private readonly token;
|
|
33
|
+
/** Local port forwards to machines' private addresses, by machine id, when access is "proxy". */
|
|
34
|
+
private readonly proxies;
|
|
35
|
+
constructor({ apiUrl, token, app, org, region, image, label, access, }?: FlyProviderOptions);
|
|
36
|
+
private api;
|
|
37
|
+
ensureApp(): Promise<void>;
|
|
38
|
+
private ensureVolume;
|
|
39
|
+
private url;
|
|
40
|
+
private closeProxy;
|
|
41
|
+
start({ id, token, image, limits, volume }: ProviderStartSpec): Promise<ProviderStarted>;
|
|
42
|
+
pause({ name }: ProviderStarted): Promise<void>;
|
|
43
|
+
resume({ name }: ProviderStarted): Promise<{
|
|
44
|
+
url: string;
|
|
45
|
+
}>;
|
|
46
|
+
stop({ name }: ProviderStarted): Promise<void>;
|
|
47
|
+
close(): void;
|
|
48
|
+
running(): Promise<Set<string>>;
|
|
49
|
+
removeVolume(name: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=fly-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fly-provider.d.ts","sourceRoot":"","sources":["../src/fly-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExF,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAYD;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,iGAAiG;IACjG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2D;gBAEvE,EACV,MAAmE,EACnE,KAAiC,EACjC,GAAkD,EAClD,GAAuC,EACvC,MAAwC,EACxC,KAA4D,EAC5D,KAAkC,EAClC,MAAM,GACP,GAAE,kBAAuB;YAaZ,GAAG;IAWX,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;YAKlB,YAAY;YAOZ,GAAG;IAejB,OAAO,CAAC,UAAU;IAOZ,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAkB,EAAE,MAAW,EAAE,MAAM,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB1G,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ3D,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD,KAAK,IAAI,IAAI;IAEP,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAK/B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMhD"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Fly Machines provider: the same four calls against api.machines.dev. Vendor
|
|
4
|
+
* truth carried through: a stopped machine restarts from a clean rootfs, so
|
|
5
|
+
* only a volume-mounted workspace survives pause and resume; guest memory is
|
|
6
|
+
* sized in 256 MiB steps; a deleted volume lingers as pending_destroy.
|
|
7
|
+
*/
|
|
8
|
+
export class FlyProvider {
|
|
9
|
+
apiUrl;
|
|
10
|
+
app;
|
|
11
|
+
org;
|
|
12
|
+
region;
|
|
13
|
+
image;
|
|
14
|
+
label;
|
|
15
|
+
access;
|
|
16
|
+
token;
|
|
17
|
+
/** Local port forwards to machines' private addresses, by machine id, when access is "proxy". */
|
|
18
|
+
proxies = new Map();
|
|
19
|
+
constructor({ apiUrl = process.env.FLY_API_HOSTNAME ?? "https://api.machines.dev", token = process.env.FLY_API_TOKEN, app = process.env.FLY_APP ?? "substrate-sandboxes", org = process.env.FLY_ORG ?? "personal", region = process.env.FLY_REGION ?? "iad", image = process.env.FLY_IMAGE ?? "substrate-node-host:local", label = "volter.substrate.sandbox", access, } = {}) {
|
|
20
|
+
if (!token)
|
|
21
|
+
throw new Error("FLY_API_TOKEN is required.");
|
|
22
|
+
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
23
|
+
this.token = token;
|
|
24
|
+
this.app = app;
|
|
25
|
+
this.org = org;
|
|
26
|
+
this.region = region;
|
|
27
|
+
this.image = image;
|
|
28
|
+
this.label = label;
|
|
29
|
+
const configured = process.env.FLY_ACCESS === "direct" ? "direct" : process.env.FLY_ACCESS === "proxy" ? "proxy" : undefined;
|
|
30
|
+
this.access = access ?? configured ?? (/api\.machines\.dev/.test(this.apiUrl) ? "proxy" : "direct");
|
|
31
|
+
}
|
|
32
|
+
async api(method, path, body) {
|
|
33
|
+
const response = await fetch(`${this.apiUrl}/v1${path}`, {
|
|
34
|
+
method,
|
|
35
|
+
headers: { authorization: `Bearer ${this.token}`, ...(body !== undefined ? { "content-type": "application/json" } : {}) },
|
|
36
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
37
|
+
});
|
|
38
|
+
const text = await response.text();
|
|
39
|
+
if (!response.ok)
|
|
40
|
+
throw new Error(`Fly ${method} ${path} -> ${response.status} ${text}`);
|
|
41
|
+
return (text ? JSON.parse(text) : undefined);
|
|
42
|
+
}
|
|
43
|
+
async ensureApp() {
|
|
44
|
+
try {
|
|
45
|
+
await this.api("GET", `/apps/${this.app}`);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
await this.api("POST", "/apps", { app_name: this.app, org_slug: this.org });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async ensureVolume(name) {
|
|
52
|
+
const volumes = await this.api("GET", `/apps/${this.app}/volumes`);
|
|
53
|
+
// A deleted volume lingers as pending_destroy; only a created one is mountable.
|
|
54
|
+
return volumes.find((volume) => volume.name === name && volume.state === "created")
|
|
55
|
+
?? this.api("POST", `/apps/${this.app}/volumes`, { name, region: this.region, size_gb: 1 });
|
|
56
|
+
}
|
|
57
|
+
async url(machine) {
|
|
58
|
+
const local = (machine.twin_local_ports ?? []).find((entry) => entry.internal === 8787);
|
|
59
|
+
if (local)
|
|
60
|
+
return `http://127.0.0.1:${local.host}`;
|
|
61
|
+
if (this.access !== "proxy")
|
|
62
|
+
return `http://[${machine.private_ip ?? ""}]:8787`;
|
|
63
|
+
const existing = this.proxies.get(machine.id);
|
|
64
|
+
if (existing)
|
|
65
|
+
return existing.url;
|
|
66
|
+
const port = 20_000 + Math.floor(Math.random() * 20_000);
|
|
67
|
+
const child = spawn("fly", ["proxy", `${port}:8787`, machine.private_ip ?? "", "-a", this.app], { stdio: ["ignore", "ignore", "pipe"] });
|
|
68
|
+
child.stderr?.on("data", () => undefined);
|
|
69
|
+
child.on("error", () => this.proxies.delete(machine.id));
|
|
70
|
+
const proxy = { child, url: `http://127.0.0.1:${port}` };
|
|
71
|
+
this.proxies.set(machine.id, proxy);
|
|
72
|
+
return proxy.url;
|
|
73
|
+
}
|
|
74
|
+
closeProxy(machineId) {
|
|
75
|
+
const proxy = this.proxies.get(machineId);
|
|
76
|
+
if (!proxy)
|
|
77
|
+
return;
|
|
78
|
+
proxy.child.kill("SIGTERM");
|
|
79
|
+
this.proxies.delete(machineId);
|
|
80
|
+
}
|
|
81
|
+
async start({ id, token, image = this.image, limits = {}, volume }) {
|
|
82
|
+
await this.ensureApp();
|
|
83
|
+
const mounts = volume ? [{ volume: (await this.ensureVolume(volume)).id, path: "/workspace" }] : [];
|
|
84
|
+
const created = await this.api("POST", `/apps/${this.app}/machines`, {
|
|
85
|
+
name: `sandbox-${id.slice(0, 12)}`,
|
|
86
|
+
region: this.region,
|
|
87
|
+
config: {
|
|
88
|
+
image,
|
|
89
|
+
env: { SUBSTRATE_ROOT: "/", SUBSTRATE_TOKEN: token, PORT: "8787", HOST: "::" },
|
|
90
|
+
services: [{ protocol: "tcp", internal_port: 8787, ports: [{ port: 8787 }] }],
|
|
91
|
+
// Fly sizes guest memory in 256 MiB steps.
|
|
92
|
+
guest: { cpu_kind: "shared", cpus: Math.max(1, Math.ceil(limits.cpus ?? 1)), memory_mb: Math.max(256, Math.ceil((limits.memoryBytes ?? 256 * 1024 * 1024) / (256 * 1_048_576)) * 256) },
|
|
93
|
+
metadata: { [this.label]: id },
|
|
94
|
+
mounts,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
await this.api("GET", `/apps/${this.app}/machines/${created.id}/wait?state=started&timeout=60`);
|
|
98
|
+
const machine = await this.api("GET", `/apps/${this.app}/machines/${created.id}`);
|
|
99
|
+
return { name: created.id, url: await this.url(machine) };
|
|
100
|
+
}
|
|
101
|
+
async pause({ name }) {
|
|
102
|
+
await this.api("POST", `/apps/${this.app}/machines/${name}/stop`);
|
|
103
|
+
await this.api("GET", `/apps/${this.app}/machines/${name}/wait?state=stopped&timeout=60`);
|
|
104
|
+
}
|
|
105
|
+
async resume({ name }) {
|
|
106
|
+
// A restarted machine is a new guest; the old forward is stale, so rebuild it.
|
|
107
|
+
this.closeProxy(name);
|
|
108
|
+
await this.api("POST", `/apps/${this.app}/machines/${name}/start`);
|
|
109
|
+
await this.api("GET", `/apps/${this.app}/machines/${name}/wait?state=started&timeout=60`);
|
|
110
|
+
return { url: await this.url(await this.api("GET", `/apps/${this.app}/machines/${name}`)) };
|
|
111
|
+
}
|
|
112
|
+
async stop({ name }) {
|
|
113
|
+
this.closeProxy(name);
|
|
114
|
+
try {
|
|
115
|
+
await this.api("DELETE", `/apps/${this.app}/machines/${name}?force=true`);
|
|
116
|
+
}
|
|
117
|
+
catch { /* already gone */ }
|
|
118
|
+
}
|
|
119
|
+
close() { for (const id of [...this.proxies.keys()])
|
|
120
|
+
this.closeProxy(id); }
|
|
121
|
+
async running() {
|
|
122
|
+
const machines = await this.api("GET", `/apps/${this.app}/machines`);
|
|
123
|
+
return new Set(machines.filter((machine) => machine.state === "started").map((machine) => machine.config?.metadata?.[this.label]).filter((value) => Boolean(value)));
|
|
124
|
+
}
|
|
125
|
+
async removeVolume(name) {
|
|
126
|
+
const volumes = await this.api("GET", `/apps/${this.app}/volumes`);
|
|
127
|
+
for (const volume of volumes.filter((candidate) => candidate.name === name)) {
|
|
128
|
+
try {
|
|
129
|
+
await this.api("DELETE", `/apps/${this.app}/volumes/${volume.id}`);
|
|
130
|
+
}
|
|
131
|
+
catch { /* gone */ }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=fly-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fly-provider.js","sourceRoot":"","sources":["../src/fly-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./client.js";
|
|
2
|
+
export * from "./manager.js";
|
|
3
|
+
export * from "./docker-provider.js";
|
|
4
|
+
export * from "./fly-provider.js";
|
|
5
|
+
export * from "./providers.js";
|
|
6
|
+
export * from "./control-plane.js";
|
|
7
|
+
export * from "./mcp.js";
|
|
8
|
+
export * from "./contract.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./client.js";
|
|
2
|
+
export * from "./manager.js";
|
|
3
|
+
export * from "./docker-provider.js";
|
|
4
|
+
export * from "./fly-provider.js";
|
|
5
|
+
export * from "./providers.js";
|
|
6
|
+
export * from "./control-plane.js";
|
|
7
|
+
export * from "./mcp.js";
|
|
8
|
+
export * from "./contract.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC"}
|