@evermore.work/plugin-cloudflare-sandbox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +48 -0
  2. package/bridge-template/Dockerfile +14 -0
  3. package/bridge-template/README.md +50 -0
  4. package/bridge-template/package.json +21 -0
  5. package/bridge-template/src/auth.test.ts +30 -0
  6. package/bridge-template/src/auth.ts +40 -0
  7. package/bridge-template/src/exec.test.ts +151 -0
  8. package/bridge-template/src/exec.ts +147 -0
  9. package/bridge-template/src/helpers.ts +39 -0
  10. package/bridge-template/src/index.ts +25 -0
  11. package/bridge-template/src/routes.test.ts +143 -0
  12. package/bridge-template/src/routes.ts +468 -0
  13. package/bridge-template/src/sandboxes.test.ts +32 -0
  14. package/bridge-template/src/sandboxes.ts +57 -0
  15. package/bridge-template/src/sessions.ts +84 -0
  16. package/bridge-template/tsconfig.json +11 -0
  17. package/bridge-template/vitest.config.ts +8 -0
  18. package/bridge-template/wrangler.jsonc +28 -0
  19. package/dist/bridge-client.d.ts +39 -0
  20. package/dist/bridge-client.d.ts.map +1 -0
  21. package/dist/bridge-client.js +232 -0
  22. package/dist/bridge-client.js.map +1 -0
  23. package/dist/config.d.ts +4 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +71 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/manifest.d.ts +4 -0
  32. package/dist/manifest.d.ts.map +1 -0
  33. package/dist/manifest.js +91 -0
  34. package/dist/manifest.js.map +1 -0
  35. package/dist/plugin.d.ts +3 -0
  36. package/dist/plugin.d.ts.map +1 -0
  37. package/dist/plugin.js +267 -0
  38. package/dist/plugin.js.map +1 -0
  39. package/dist/types.d.ts +91 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/worker.d.ts +3 -0
  44. package/dist/worker.d.ts.map +1 -0
  45. package/dist/worker.js +5 -0
  46. package/dist/worker.js.map +1 -0
  47. package/package.json +51 -0
@@ -0,0 +1,84 @@
1
+ import type { Sandbox as CloudflareSandbox } from "@cloudflare/sandbox";
2
+ import { DEFAULT_SESSION_ID } from "./sandboxes.js";
3
+
4
+ export type SessionStrategy = "named" | "default";
5
+
6
+ export interface ResolvedSession {
7
+ exec(
8
+ command: string,
9
+ options?: {
10
+ args?: string[];
11
+ cwd?: string;
12
+ env?: Record<string, string>;
13
+ stdin?: string | null;
14
+ timeout?: number;
15
+ stream?: boolean;
16
+ onOutput?: (stream: "stdout" | "stderr", data: string) => void | Promise<void>;
17
+ },
18
+ ): Promise<{ success?: boolean; stdout?: string; stderr?: string; exitCode?: number | null }>;
19
+ }
20
+
21
+ export async function getNamedSession(
22
+ sandbox: CloudflareSandbox,
23
+ options: {
24
+ sessionId?: string;
25
+ cwd?: string;
26
+ env?: Record<string, string>;
27
+ timeoutMs?: number;
28
+ },
29
+ ): Promise<ResolvedSession> {
30
+ const sessionId = options.sessionId?.trim() || DEFAULT_SESSION_ID;
31
+ try {
32
+ return await sandbox.getSession(sessionId);
33
+ } catch (err) {
34
+ // Only fall through to `createSession` for the "session not found" case.
35
+ // The Sandbox SDK currently surfaces missing-session as an Error whose
36
+ // message contains "not found" / "does not exist"; any other failure
37
+ // (quota exceeded, sandbox destroyed mid-request, malformed ID) should
38
+ // bubble up so callers see the real cause instead of a confusing
39
+ // secondary `createSession` error that hides the root cause.
40
+ if (!isSessionNotFoundError(err)) throw err;
41
+ // Create the session without pinning it to a workspace path up front.
42
+ // Workspace preparation may be the first thing we do with the session.
43
+ return await sandbox.createSession({
44
+ id: sessionId,
45
+ env: options.env,
46
+ commandTimeoutMs: options.timeoutMs,
47
+ });
48
+ }
49
+ }
50
+
51
+ function isSessionNotFoundError(err: unknown): boolean {
52
+ if (!err) return false;
53
+ const message =
54
+ err instanceof Error ? err.message : typeof err === "string" ? err : "";
55
+ return /not\s*found|does\s*not\s*exist|no\s+such\s+session/i.test(message);
56
+ }
57
+
58
+ export async function resolveExecutionTarget(
59
+ sandbox: CloudflareSandbox,
60
+ options: {
61
+ sessionStrategy: SessionStrategy;
62
+ sessionId?: string;
63
+ cwd?: string;
64
+ env?: Record<string, string>;
65
+ timeoutMs?: number;
66
+ },
67
+ ): Promise<ResolvedSession | CloudflareSandbox> {
68
+ if (options.sessionStrategy === "default") return sandbox;
69
+ return await getNamedSession(sandbox, options);
70
+ }
71
+
72
+ export async function cleanupTimedOutExecution(
73
+ sandbox: CloudflareSandbox,
74
+ options: {
75
+ sessionStrategy: SessionStrategy;
76
+ sessionId?: string;
77
+ },
78
+ ): Promise<void> {
79
+ if (options.sessionStrategy === "default") {
80
+ await sandbox.destroy().catch(() => undefined);
81
+ return;
82
+ }
83
+ await sandbox.deleteSession(options.sessionId?.trim() || DEFAULT_SESSION_ID).catch(() => undefined);
84
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "lib": ["ES2023", "WebWorker"],
6
+ "types": ["@cloudflare/workers-types"],
7
+ "noEmit": true
8
+ },
9
+ "include": ["src/**/*.ts"],
10
+ "exclude": ["src/**/*.test.ts"]
11
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ["src/**/*.test.ts"],
6
+ environment: "node",
7
+ },
8
+ });
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "evermore-cloudflare-sandbox-bridge",
3
+ "main": "src/index.ts",
4
+ "compatibility_date": "2026-05-09",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "containers": [
7
+ {
8
+ "class_name": "Sandbox",
9
+ "image": "./Dockerfile",
10
+ "instance_type": "lite",
11
+ "max_instances": 10
12
+ }
13
+ ],
14
+ "durable_objects": {
15
+ "bindings": [
16
+ {
17
+ "class_name": "Sandbox",
18
+ "name": "Sandbox"
19
+ }
20
+ ]
21
+ },
22
+ "migrations": [
23
+ {
24
+ "tag": "v1",
25
+ "new_sqlite_classes": ["Sandbox"]
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,39 @@
1
+ import type { CloudflareBridgeAcquireLeaseRequest, CloudflareBridgeExecuteRequest, CloudflareBridgeExecuteResponse, CloudflareBridgeHealthResponse, CloudflareBridgeLeaseResponse, CloudflareBridgeProbeRequest, CloudflareBridgeProbeResponse, CloudflareBridgeReleaseLeaseRequest, CloudflareBridgeResumeLeaseRequest, CloudflareDriverConfig } from "./types.js";
2
+ interface BridgeClientHeaders {
3
+ environmentId?: string;
4
+ runId?: string;
5
+ issueId?: string | null;
6
+ }
7
+ interface BridgeClientOptions {
8
+ config: CloudflareDriverConfig;
9
+ }
10
+ interface BridgeExecuteOptions {
11
+ onOutput?: (stream: "stdout" | "stderr", chunk: string) => void | Promise<void>;
12
+ }
13
+ export declare class CloudflareBridgeError extends Error {
14
+ readonly status: number;
15
+ readonly code: string | null;
16
+ readonly details: unknown;
17
+ constructor(input: {
18
+ status: number;
19
+ code?: string | null;
20
+ message: string;
21
+ details?: unknown;
22
+ });
23
+ }
24
+ export declare function resolveRequestTimeoutMs(config: CloudflareDriverConfig, path: string, init: RequestInit): number;
25
+ export declare function createCloudflareBridgeClient(options: BridgeClientOptions): {
26
+ health(extraHeaders?: BridgeClientHeaders): Promise<CloudflareBridgeHealthResponse>;
27
+ probe(body: CloudflareBridgeProbeRequest, extraHeaders?: BridgeClientHeaders): Promise<CloudflareBridgeProbeResponse>;
28
+ acquireLease(body: CloudflareBridgeAcquireLeaseRequest, extraHeaders?: BridgeClientHeaders): Promise<CloudflareBridgeLeaseResponse>;
29
+ resumeLease(body: CloudflareBridgeResumeLeaseRequest, extraHeaders?: BridgeClientHeaders): Promise<CloudflareBridgeLeaseResponse>;
30
+ releaseLease(body: CloudflareBridgeReleaseLeaseRequest, extraHeaders?: BridgeClientHeaders): Promise<{
31
+ ok: true;
32
+ }>;
33
+ destroyLease(providerLeaseId: string, extraHeaders?: BridgeClientHeaders): Promise<{
34
+ ok: true;
35
+ }>;
36
+ execute(body: CloudflareBridgeExecuteRequest, extraHeaders?: BridgeClientHeaders, options?: BridgeExecuteOptions): Promise<CloudflareBridgeExecuteResponse>;
37
+ };
38
+ export {};
39
+ //# sourceMappingURL=bridge-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,8BAA8B,EAC9B,+BAA+B,EAC/B,8BAA8B,EAC9B,6BAA6B,EAC7B,4BAA4B,EAC5B,6BAA6B,EAC7B,mCAAmC,EACnC,kCAAkC,EAClC,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjF;AAYD,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;CAOhG;AAsCD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,EAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,GAChB,MAAM,CAQR;AA4KD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,mBAAmB;0BAK/C,mBAAmB,GAAG,OAAO,CAAC,8BAA8B,CAAC;gBAIvE,4BAA4B,iBAAiB,mBAAmB,GAAG,OAAO,CAAC,6BAA6B,CAAC;uBAU7G,mCAAmC,iBAC1B,mBAAmB,GACjC,OAAO,CAAC,6BAA6B,CAAC;sBAUjC,kCAAkC,iBACzB,mBAAmB,GACjC,OAAO,CAAC,6BAA6B,CAAC;uBAUjC,mCAAmC,iBAC1B,mBAAmB,GACjC,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC;kCASM,MAAM,iBAAiB,mBAAmB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC;kBAUxF,8BAA8B,iBACrB,mBAAmB,YACxB,oBAAoB,GAC7B,OAAO,CAAC,+BAA+B,CAAC;EAkB9C"}
@@ -0,0 +1,232 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ export class CloudflareBridgeError extends Error {
5
+ status;
6
+ code;
7
+ details;
8
+ constructor(input) {
9
+ super(input.message);
10
+ this.name = "CloudflareBridgeError";
11
+ this.status = input.status;
12
+ this.code = input.code ?? null;
13
+ this.details = input.details;
14
+ }
15
+ }
16
+ function buildHeaders(config, extra = {}) {
17
+ const headers = new Headers();
18
+ headers.set("Authorization", `Bearer ${config.bridgeAuthToken}`);
19
+ headers.set("Content-Type", "application/json");
20
+ if (extra.environmentId)
21
+ headers.set("X-Evermore-Environment-Id", extra.environmentId);
22
+ if (extra.runId)
23
+ headers.set("X-Evermore-Run-Id", extra.runId);
24
+ if (extra.issueId)
25
+ headers.set("X-Evermore-Issue-Id", extra.issueId);
26
+ return headers;
27
+ }
28
+ async function parseJson(response) {
29
+ const contentType = response.headers.get("content-type") ?? "";
30
+ if (!contentType.toLowerCase().includes("application/json")) {
31
+ return null;
32
+ }
33
+ return await response.json();
34
+ }
35
+ function encodeExecuteRequestBody(body, options) {
36
+ return JSON.stringify({
37
+ ...body,
38
+ streamOutput: typeof options?.onOutput === "function",
39
+ });
40
+ }
41
+ function parseExecuteTimeoutMs(body) {
42
+ if (typeof body !== "string")
43
+ return null;
44
+ try {
45
+ const parsed = JSON.parse(body);
46
+ const timeoutMs = Number(parsed.timeoutMs);
47
+ return Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.trunc(timeoutMs) : null;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ export function resolveRequestTimeoutMs(config, path, init) {
54
+ if (!path.endsWith("/exec")) {
55
+ return config.bridgeRequestTimeoutMs;
56
+ }
57
+ const requestedTimeoutMs = parseExecuteTimeoutMs(init.body);
58
+ return requestedTimeoutMs === null
59
+ ? config.bridgeRequestTimeoutMs
60
+ : Math.max(config.bridgeRequestTimeoutMs, requestedTimeoutMs);
61
+ }
62
+ async function requestJson(config, path, init, extraHeaders = {}) {
63
+ const controller = new AbortController();
64
+ const requestTimeoutMs = resolveRequestTimeoutMs(config, path, init);
65
+ const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
66
+ const baseUrl = config.bridgeBaseUrl.replace(/\/+$/, "");
67
+ try {
68
+ const response = await fetch(`${baseUrl}${path}`, {
69
+ ...init,
70
+ headers: buildHeaders(config, extraHeaders),
71
+ signal: controller.signal,
72
+ });
73
+ const body = await parseJson(response);
74
+ if (!response.ok) {
75
+ const errorBody = isRecord(body) ? body : {};
76
+ throw new CloudflareBridgeError({
77
+ status: response.status,
78
+ code: typeof errorBody.error === "string" ? errorBody.error : null,
79
+ message: typeof errorBody.message === "string" && errorBody.message.trim().length > 0
80
+ ? errorBody.message
81
+ : `Cloudflare sandbox bridge request failed with HTTP ${response.status}.`,
82
+ details: errorBody.details,
83
+ });
84
+ }
85
+ return body;
86
+ }
87
+ catch (error) {
88
+ if (error instanceof CloudflareBridgeError)
89
+ throw error;
90
+ if (error?.name === "AbortError") {
91
+ throw new Error(`Cloudflare sandbox bridge request timed out after ${requestTimeoutMs}ms.`);
92
+ }
93
+ throw error;
94
+ }
95
+ finally {
96
+ clearTimeout(timeout);
97
+ }
98
+ }
99
+ async function requestResponse(config, path, init, extraHeaders = {}) {
100
+ const controller = new AbortController();
101
+ const requestTimeoutMs = resolveRequestTimeoutMs(config, path, init);
102
+ const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
103
+ const baseUrl = config.bridgeBaseUrl.replace(/\/+$/, "");
104
+ try {
105
+ const response = await fetch(`${baseUrl}${path}`, {
106
+ ...init,
107
+ headers: buildHeaders(config, extraHeaders),
108
+ signal: controller.signal,
109
+ });
110
+ if (!response.ok) {
111
+ const body = await parseJson(response);
112
+ const errorBody = isRecord(body) ? body : {};
113
+ throw new CloudflareBridgeError({
114
+ status: response.status,
115
+ code: typeof errorBody.error === "string" ? errorBody.error : null,
116
+ message: typeof errorBody.message === "string" && errorBody.message.trim().length > 0
117
+ ? errorBody.message
118
+ : `Cloudflare sandbox bridge request failed with HTTP ${response.status}.`,
119
+ details: errorBody.details,
120
+ });
121
+ }
122
+ return response;
123
+ }
124
+ catch (error) {
125
+ if (error instanceof CloudflareBridgeError)
126
+ throw error;
127
+ if (error?.name === "AbortError") {
128
+ throw new Error(`Cloudflare sandbox bridge request timed out after ${requestTimeoutMs}ms.`);
129
+ }
130
+ throw error;
131
+ }
132
+ finally {
133
+ clearTimeout(timeout);
134
+ }
135
+ }
136
+ function parseSseChunk(buffer) {
137
+ const normalized = buffer.replace(/\r\n/g, "\n");
138
+ const frames = normalized.split("\n\n");
139
+ const rest = frames.pop() ?? "";
140
+ const events = [];
141
+ for (const frame of frames) {
142
+ let event = "message";
143
+ const dataLines = [];
144
+ for (const line of frame.split("\n")) {
145
+ if (line.startsWith("event:")) {
146
+ event = line.slice("event:".length).trim() || "message";
147
+ continue;
148
+ }
149
+ if (line.startsWith("data:")) {
150
+ dataLines.push(line.slice("data:".length).trimStart());
151
+ }
152
+ }
153
+ events.push({
154
+ event,
155
+ data: dataLines.join("\n"),
156
+ });
157
+ }
158
+ return { events, rest };
159
+ }
160
+ async function consumeExecuteEventStream(response, options) {
161
+ if (!response.body) {
162
+ throw new Error("Cloudflare sandbox bridge streaming response had no body.");
163
+ }
164
+ const reader = response.body.getReader();
165
+ const decoder = new TextDecoder();
166
+ let buffer = "";
167
+ let result = null;
168
+ while (true) {
169
+ const { done, value } = await reader.read();
170
+ buffer += decoder.decode(value ?? new Uint8Array(), { stream: !done });
171
+ const parsed = parseSseChunk(done && buffer.length > 0 ? `${buffer}\n\n` : buffer);
172
+ buffer = parsed.rest;
173
+ for (const event of parsed.events) {
174
+ if (event.event === "stdout" || event.event === "stderr") {
175
+ const payload = JSON.parse(event.data);
176
+ const chunk = typeof payload.data === "string" ? payload.data : "";
177
+ if (chunk) {
178
+ await options.onOutput?.(event.event, chunk);
179
+ }
180
+ continue;
181
+ }
182
+ if (event.event === "complete") {
183
+ result = JSON.parse(event.data);
184
+ continue;
185
+ }
186
+ if (event.event === "error") {
187
+ const payload = JSON.parse(event.data);
188
+ const message = typeof payload.error === "string" && payload.error.trim().length > 0
189
+ ? payload.error
190
+ : "Cloudflare sandbox bridge streaming command failed.";
191
+ throw new Error(message);
192
+ }
193
+ }
194
+ if (done)
195
+ break;
196
+ }
197
+ if (result)
198
+ return result;
199
+ throw new Error("Cloudflare sandbox bridge streaming response ended without a completion event.");
200
+ }
201
+ export function createCloudflareBridgeClient(options) {
202
+ const { config } = options;
203
+ const apiPrefix = "/api/evermore-sandbox/v1";
204
+ return {
205
+ health(extraHeaders) {
206
+ return requestJson(config, `${apiPrefix}/health`, { method: "GET" }, extraHeaders);
207
+ },
208
+ probe(body, extraHeaders) {
209
+ return requestJson(config, `${apiPrefix}/probe`, { method: "POST", body: JSON.stringify(body) }, extraHeaders);
210
+ },
211
+ acquireLease(body, extraHeaders) {
212
+ return requestJson(config, `${apiPrefix}/leases/acquire`, { method: "POST", body: JSON.stringify(body) }, extraHeaders);
213
+ },
214
+ resumeLease(body, extraHeaders) {
215
+ return requestJson(config, `${apiPrefix}/leases/resume`, { method: "POST", body: JSON.stringify(body) }, extraHeaders);
216
+ },
217
+ releaseLease(body, extraHeaders) {
218
+ return requestJson(config, `${apiPrefix}/leases/release`, { method: "POST", body: JSON.stringify(body) }, extraHeaders);
219
+ },
220
+ destroyLease(providerLeaseId, extraHeaders) {
221
+ return requestJson(config, `${apiPrefix}/leases/${encodeURIComponent(providerLeaseId)}`, { method: "DELETE" }, extraHeaders);
222
+ },
223
+ execute(body, extraHeaders, options) {
224
+ const encodedBody = encodeExecuteRequestBody(body, options);
225
+ if (typeof options?.onOutput === "function") {
226
+ return requestResponse(config, `${apiPrefix}/exec`, { method: "POST", body: encodedBody }, extraHeaders).then((response) => consumeExecuteEventStream(response, options));
227
+ }
228
+ return requestJson(config, `${apiPrefix}/exec`, { method: "POST", body: encodedBody }, extraHeaders);
229
+ },
230
+ };
231
+ }
232
+ //# sourceMappingURL=bridge-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-client.js","sourceRoot":"","sources":["../src/bridge-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { CloudflareDriverConfig } from "./types.js";
2
+ export declare function parseCloudflareDriverConfig(raw: Record<string, unknown>): CloudflareDriverConfig;
3
+ export declare function validateCloudflareDriverConfig(config: CloudflareDriverConfig): string[];
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAyBzD,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,sBAAsB,CAehG;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,EAAE,CAyCvF"}
package/dist/config.js ADDED
@@ -0,0 +1,71 @@
1
+ const DEFAULT_REQUESTED_CWD = "/workspace/evermore";
2
+ const DEFAULT_SLEEP_AFTER = "10m";
3
+ const DEFAULT_TIMEOUT_MS = 300_000;
4
+ const DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS = 30_000;
5
+ const LOCALHOST_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1"]);
6
+ function readTrimmedString(value) {
7
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
8
+ }
9
+ function readBoolean(value, fallback) {
10
+ return value === undefined ? fallback : value === true;
11
+ }
12
+ function readInteger(value, fallback) {
13
+ const parsed = Number(value);
14
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : fallback;
15
+ }
16
+ function isLocalBridgeHost(url) {
17
+ return LOCALHOST_HOSTNAMES.has(url.hostname);
18
+ }
19
+ export function parseCloudflareDriverConfig(raw) {
20
+ return {
21
+ bridgeBaseUrl: readTrimmedString(raw.bridgeBaseUrl) ?? "",
22
+ bridgeAuthToken: readTrimmedString(raw.bridgeAuthToken) ?? "",
23
+ reuseLease: readBoolean(raw.reuseLease, false),
24
+ keepAlive: readBoolean(raw.keepAlive, false),
25
+ sleepAfter: readTrimmedString(raw.sleepAfter) ?? DEFAULT_SLEEP_AFTER,
26
+ normalizeId: readBoolean(raw.normalizeId, true),
27
+ requestedCwd: readTrimmedString(raw.requestedCwd) ?? DEFAULT_REQUESTED_CWD,
28
+ sessionStrategy: raw.sessionStrategy === "default" ? "default" : "named",
29
+ sessionId: readTrimmedString(raw.sessionId) ?? "evermore",
30
+ timeoutMs: readInteger(raw.timeoutMs, DEFAULT_TIMEOUT_MS),
31
+ bridgeRequestTimeoutMs: readInteger(raw.bridgeRequestTimeoutMs, DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS),
32
+ previewHostname: readTrimmedString(raw.previewHostname),
33
+ };
34
+ }
35
+ export function validateCloudflareDriverConfig(config) {
36
+ const errors = [];
37
+ if (!config.bridgeBaseUrl) {
38
+ errors.push("Cloudflare sandbox environments require bridgeBaseUrl.");
39
+ }
40
+ else {
41
+ try {
42
+ const url = new URL(config.bridgeBaseUrl);
43
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocalBridgeHost(url))) {
44
+ errors.push("bridgeBaseUrl must use HTTPS unless it points at localhost.");
45
+ }
46
+ }
47
+ catch {
48
+ errors.push("bridgeBaseUrl must be a valid URL.");
49
+ }
50
+ }
51
+ if (!config.bridgeAuthToken) {
52
+ errors.push("Cloudflare sandbox environments require bridgeAuthToken.");
53
+ }
54
+ if (config.reuseLease && !config.keepAlive) {
55
+ errors.push("reuseLease requires keepAlive for Cloudflare sandboxes.");
56
+ }
57
+ if (config.timeoutMs < 1 || config.timeoutMs > 86_400_000) {
58
+ errors.push("timeoutMs must be between 1 and 86400000.");
59
+ }
60
+ if (config.bridgeRequestTimeoutMs < 1 || config.bridgeRequestTimeoutMs > 86_400_000) {
61
+ errors.push("bridgeRequestTimeoutMs must be between 1 and 86400000.");
62
+ }
63
+ if (!config.requestedCwd.startsWith("/")) {
64
+ errors.push("requestedCwd must be an absolute POSIX path.");
65
+ }
66
+ if (config.sessionStrategy === "named" && config.sessionId.trim().length === 0) {
67
+ errors.push("sessionId is required when sessionStrategy is named.");
68
+ }
69
+ return errors;
70
+ }
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { default as manifest } from "./manifest.js";
2
+ export { default as plugin } from "./plugin.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { default as manifest } from "./manifest.js";
2
+ export { default as plugin } from "./plugin.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EvermorePluginManifestV1 } from "@evermore.work/plugin-sdk";
2
+ declare const manifest: EvermorePluginManifestV1;
3
+ export default manifest;
4
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAK1E,QAAA,MAAM,QAAQ,EAAE,wBAyFf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,91 @@
1
+ const PLUGIN_ID = "evermore.cloudflare-sandbox-provider";
2
+ const PLUGIN_VERSION = "0.1.0";
3
+ const manifest = {
4
+ id: PLUGIN_ID,
5
+ apiVersion: 1,
6
+ version: PLUGIN_VERSION,
7
+ displayName: "Cloudflare Sandbox Provider",
8
+ description: "First-party sandbox provider plugin that provisions Cloudflare sandboxes through an operator-deployed Worker bridge.",
9
+ author: "Evermore",
10
+ categories: ["automation"],
11
+ capabilities: ["environment.drivers.register"],
12
+ entrypoints: {
13
+ worker: "./dist/worker.js",
14
+ },
15
+ environmentDrivers: [
16
+ {
17
+ driverKey: "cloudflare",
18
+ kind: "sandbox_provider",
19
+ displayName: "Cloudflare Sandbox",
20
+ description: "Runs Evermore sandbox environments through a Cloudflare Worker bridge backed by the Sandbox SDK and Durable Objects.",
21
+ configSchema: {
22
+ type: "object",
23
+ properties: {
24
+ bridgeBaseUrl: {
25
+ type: "string",
26
+ format: "uri",
27
+ description: "Base URL of the operator-deployed Cloudflare Worker bridge.",
28
+ },
29
+ bridgeAuthToken: {
30
+ type: "string",
31
+ format: "secret-ref",
32
+ description: "Bearer token used by the provider plugin when calling the Cloudflare bridge. Pasted values are stored as company secrets.",
33
+ },
34
+ reuseLease: {
35
+ type: "boolean",
36
+ default: false,
37
+ description: "Reuse a sandbox by environment ID instead of creating one per run.",
38
+ },
39
+ keepAlive: {
40
+ type: "boolean",
41
+ default: false,
42
+ description: "Prevent Cloudflare from idling the container between requests.",
43
+ },
44
+ sleepAfter: {
45
+ type: "string",
46
+ default: "10m",
47
+ description: "Idle timeout passed to getSandbox(). Ignored when keepAlive is true.",
48
+ },
49
+ normalizeId: {
50
+ type: "boolean",
51
+ default: true,
52
+ description: "Lowercase and normalize sandbox IDs for operator-friendly naming.",
53
+ },
54
+ requestedCwd: {
55
+ type: "string",
56
+ default: "/workspace/evermore",
57
+ description: "Workspace directory to create inside the sandbox lease.",
58
+ },
59
+ sessionStrategy: {
60
+ type: "string",
61
+ enum: ["named", "default"],
62
+ default: "named",
63
+ description: "Whether to run commands in a stable named session or the default session.",
64
+ },
65
+ sessionId: {
66
+ type: "string",
67
+ default: "evermore",
68
+ description: "Named Cloudflare session ID used when sessionStrategy is named.",
69
+ },
70
+ timeoutMs: {
71
+ type: "number",
72
+ default: 300000,
73
+ description: "Default per-command timeout passed through to the bridge.",
74
+ },
75
+ bridgeRequestTimeoutMs: {
76
+ type: "number",
77
+ default: 30000,
78
+ description: "HTTP timeout for plugin-to-bridge requests.",
79
+ },
80
+ previewHostname: {
81
+ type: "string",
82
+ description: "Optional hostname reserved for future preview URL support.",
83
+ },
84
+ },
85
+ required: ["bridgeBaseUrl", "bridgeAuthToken"],
86
+ },
87
+ },
88
+ ],
89
+ };
90
+ export default manifest;
91
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,sCAAsC,CAAC;AACzD,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,MAAM,QAAQ,GAA6B;IACzC,EAAE,EAAE,SAAS;IACb,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,cAAc;IACvB,WAAW,EAAE,6BAA6B;IAC1C,WAAW,EACT,sHAAsH;IACxH,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,CAAC,YAAY,CAAC;IAC1B,YAAY,EAAE,CAAC,8BAA8B,CAAC;IAC9C,WAAW,EAAE;QACX,MAAM,EAAE,kBAAkB;KAC3B;IACD,kBAAkB,EAAE;QAClB;YACE,SAAS,EAAE,YAAY;YACvB,IAAI,EAAE,kBAAkB;YACxB,WAAW,EAAE,oBAAoB;YACjC,WAAW,EACT,sHAAsH;YACxH,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,aAAa,EAAE;wBACb,IAAI,EAAE,QAAQ;wBACd,MAAM,EAAE,KAAK;wBACb,WAAW,EAAE,6DAA6D;qBAC3E;oBACD,eAAe,EAAE;wBACf,IAAI,EAAE,QAAQ;wBACd,MAAM,EAAE,YAAY;wBACpB,WAAW,EACT,2HAA2H;qBAC9H;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,KAAK;wBACd,WAAW,EAAE,oEAAoE;qBAClF;oBACD,SAAS,EAAE;wBACT,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,KAAK;wBACd,WAAW,EAAE,gEAAgE;qBAC9E;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,KAAK;wBACd,WAAW,EAAE,sEAAsE;qBACpF;oBACD,WAAW,EAAE;wBACX,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,IAAI;wBACb,WAAW,EAAE,mEAAmE;qBACjF;oBACD,YAAY,EAAE;wBACZ,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,qBAAqB;wBAC9B,WAAW,EAAE,yDAAyD;qBACvE;oBACD,eAAe,EAAE;wBACf,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;wBAC1B,OAAO,EAAE,OAAO;wBAChB,WAAW,EAAE,2EAA2E;qBACzF;oBACD,SAAS,EAAE;wBACT,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,UAAU;wBACnB,WAAW,EAAE,iEAAiE;qBAC/E;oBACD,SAAS,EAAE;wBACT,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,MAAM;wBACf,WAAW,EAAE,2DAA2D;qBACzE;oBACD,sBAAsB,EAAE;wBACtB,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,KAAK;wBACd,WAAW,EAAE,6CAA6C;qBAC3D;oBACD,eAAe,EAAE;wBACf,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,4DAA4D;qBAC1E;iBACF;gBACD,QAAQ,EAAE,CAAC,eAAe,EAAE,iBAAiB,CAAC;aAC/C;SACF;KACF;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const plugin: import("@evermore.work/plugin-sdk").EvermorePlugin;
2
+ export default plugin;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAkIA,QAAA,MAAM,MAAM,oDA0NV,CAAC;AAEH,eAAe,MAAM,CAAC"}