@madarco/agentbox 0.16.0 → 0.17.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/CHANGELOG.md +37 -0
- package/dist/{_cloud-attach-5KJWOASL.js → _cloud-attach-RUD4W33P.js} +5 -4
- package/dist/{chunk-QXFNLKJJ.js → chunk-3JXSW6PN.js} +36 -24
- package/dist/chunk-3JXSW6PN.js.map +1 -0
- package/dist/{chunk-3WCEB6RE.js → chunk-46NXCJ6Z.js} +45 -34
- package/dist/{chunk-3WCEB6RE.js.map → chunk-46NXCJ6Z.js.map} +1 -1
- package/dist/{chunk-DBBUDKKB.js → chunk-6QFPYU4Z.js} +3 -3
- package/dist/{chunk-GXJNJUEV.js → chunk-HFQZJO73.js} +2 -2
- package/dist/{chunk-SENASAU4.js → chunk-IA6YHH52.js} +89 -81
- package/dist/chunk-IA6YHH52.js.map +1 -0
- package/dist/{chunk-PIK47622.js → chunk-KTDJ5JTE.js} +3 -3
- package/dist/chunk-WJFZJZIM.js +24 -0
- package/dist/{chunk-NW2UZQV6.js → chunk-XWBFWUY2.js} +23 -19
- package/dist/chunk-XWBFWUY2.js.map +1 -0
- package/dist/{chunk-SB4QTF2T.js → chunk-YUGLNTT2.js} +7 -7
- package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js → cloud-poller-SUNA6ZQC-7M5LJHHE.js} +2 -1
- package/dist/{dist-4IQFJJQI.js → dist-BO2R55FX.js} +6 -5
- package/dist/{dist-4IQFJJQI.js.map → dist-BO2R55FX.js.map} +1 -1
- package/dist/{dist-VHI5QOSQ.js → dist-CNABE32V.js} +6 -5
- package/dist/{dist-VHI5QOSQ.js.map → dist-CNABE32V.js.map} +1 -1
- package/dist/{dist-XC47DSCR.js → dist-CROGTJ7N.js} +6 -5
- package/dist/{dist-XC47DSCR.js.map → dist-CROGTJ7N.js.map} +1 -1
- package/dist/{dist-SL2QSMBE.js → dist-TEKY3GFT.js} +6 -5
- package/dist/{dist-SL2QSMBE.js.map → dist-TEKY3GFT.js.map} +1 -1
- package/dist/{dist-7YB7BMNG.js → dist-VAATGBAR.js} +4 -3
- package/dist/index.js +2254 -2064
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-2LANTRL7.js → prepared-state-MQHD3M5F-OVABNV66.js} +3 -2
- package/dist/prepared-state-MQHD3M5F-OVABNV66.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-NW2UZQV6.js.map +0 -1
- package/dist/chunk-QXFNLKJJ.js.map +0 -1
- package/dist/chunk-SENASAU4.js.map +0 -1
- /package/dist/{_cloud-attach-5KJWOASL.js.map → _cloud-attach-RUD4W33P.js.map} +0 -0
- /package/dist/{chunk-DBBUDKKB.js.map → chunk-6QFPYU4Z.js.map} +0 -0
- /package/dist/{chunk-GXJNJUEV.js.map → chunk-HFQZJO73.js.map} +0 -0
- /package/dist/{chunk-PIK47622.js.map → chunk-KTDJ5JTE.js.map} +0 -0
- /package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js.map → chunk-WJFZJZIM.js.map} +0 -0
- /package/dist/{chunk-SB4QTF2T.js.map → chunk-YUGLNTT2.js.map} +0 -0
- /package/dist/{dist-7YB7BMNG.js.map → cloud-poller-SUNA6ZQC-7M5LJHHE.js.map} +0 -0
- /package/dist/{prepared-state-MQHD3M5F-2LANTRL7.js.map → dist-VAATGBAR.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_RELAY_PORT,
|
|
4
4
|
loadEffectiveConfig,
|
|
5
5
|
readBoxStatus
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HFQZJO73.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/_cloud-attach.ts
|
|
9
9
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -20,26 +20,26 @@ function isKnownProvider(name) {
|
|
|
20
20
|
async function getProvider(name) {
|
|
21
21
|
switch (name) {
|
|
22
22
|
case "docker": {
|
|
23
|
-
const mod = await import("./dist-
|
|
23
|
+
const mod = await import("./dist-VAATGBAR.js");
|
|
24
24
|
return mod.dockerProvider;
|
|
25
25
|
}
|
|
26
26
|
case "daytona": {
|
|
27
|
-
const mod = await import("./dist-
|
|
27
|
+
const mod = await import("./dist-TEKY3GFT.js");
|
|
28
28
|
await mod.ensureDaytonaCredentials();
|
|
29
29
|
return mod.daytonaProvider;
|
|
30
30
|
}
|
|
31
31
|
case "hetzner": {
|
|
32
|
-
const mod = await import("./dist-
|
|
32
|
+
const mod = await import("./dist-CNABE32V.js");
|
|
33
33
|
await mod.ensureHetznerCredentials();
|
|
34
34
|
return mod.hetznerProvider;
|
|
35
35
|
}
|
|
36
36
|
case "vercel": {
|
|
37
|
-
const mod = await import("./dist-
|
|
37
|
+
const mod = await import("./dist-CROGTJ7N.js");
|
|
38
38
|
await mod.ensureVercelCredentials();
|
|
39
39
|
return mod.vercelProvider;
|
|
40
40
|
}
|
|
41
41
|
case "e2b": {
|
|
42
|
-
const mod = await import("./dist-
|
|
42
|
+
const mod = await import("./dist-BO2R55FX.js");
|
|
43
43
|
await mod.ensureE2bCredentials();
|
|
44
44
|
return mod.e2bProvider;
|
|
45
45
|
}
|
|
@@ -2173,4 +2173,4 @@ export {
|
|
|
2173
2173
|
cloudAgentAttach,
|
|
2174
2174
|
cloudAgentStartDetached
|
|
2175
2175
|
};
|
|
2176
|
-
//# sourceMappingURL=chunk-
|
|
2176
|
+
//# sourceMappingURL=chunk-YUGLNTT2.js.map
|
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
CloudBoxPoller,
|
|
4
4
|
CloudBoxPollers
|
|
5
5
|
} from "./chunk-G3H2L3O2.js";
|
|
6
|
+
import "./chunk-WJFZJZIM.js";
|
|
6
7
|
export {
|
|
7
8
|
CloudBoxPoller,
|
|
8
9
|
CloudBoxPollers
|
|
9
10
|
};
|
|
10
|
-
//# sourceMappingURL=cloud-poller-SUNA6ZQC-
|
|
11
|
+
//# sourceMappingURL=cloud-poller-SUNA6ZQC-7M5LJHHE.js.map
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
secretsPath,
|
|
20
20
|
updatePreparedState,
|
|
21
21
|
writePreparedState
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-XWBFWUY2.js";
|
|
23
23
|
import {
|
|
24
24
|
createCloudProvider,
|
|
25
25
|
currentCloudBaseFingerprint,
|
|
@@ -28,14 +28,15 @@ import {
|
|
|
28
28
|
renderInnerCommand,
|
|
29
29
|
resolveCloudCheckpoint,
|
|
30
30
|
writeCloudCheckpointManifest
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-KTDJ5JTE.js";
|
|
32
|
+
import "./chunk-HFQZJO73.js";
|
|
33
33
|
import {
|
|
34
34
|
computeContextSha256,
|
|
35
35
|
readCliStamp,
|
|
36
36
|
recordBox
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-6QFPYU4Z.js";
|
|
38
38
|
import "./chunk-G3H2L3O2.js";
|
|
39
|
+
import "./chunk-WJFZJZIM.js";
|
|
39
40
|
|
|
40
41
|
// ../../packages/sandbox-e2b/dist/index.js
|
|
41
42
|
import { readFile } from "fs/promises";
|
|
@@ -659,4 +660,4 @@ export {
|
|
|
659
660
|
updatePreparedState,
|
|
660
661
|
writePreparedState
|
|
661
662
|
};
|
|
662
|
-
//# sourceMappingURL=dist-
|
|
663
|
+
//# sourceMappingURL=dist-BO2R55FX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/sandbox-e2b/src/index.ts","../../../packages/sandbox-e2b/src/backend.ts","../../../packages/sandbox-e2b/src/retry.ts","../../../packages/sandbox-e2b/src/prepare.ts","../../../packages/sandbox-e2b/src/build-attach.ts"],"sourcesContent":["/**\n * The E2B sandbox provider. A thin `CloudBackend` over the `e2b` v2 SDK,\n * composed via `@agentbox/sandbox-cloud`'s `createCloudProvider` for\n * everything provider-agnostic (workspace seeding, ctl launch, state, relay\n * polling).\n *\n * Three capabilities are overridden on top of the cloud scaffold:\n * - `prepare` — bake the custom base template via Template.build\n * (`agentbox prepare --provider e2b`).\n * - `buildAttach` — SDK-streaming PTY bridge (E2B has no SSH).\n * - `checkpoint` — store the E2B snapshot id (template id) in the manifest\n * so restore boots from it (`Sandbox.createSnapshot` produces an\n * id-addressed reusable snapshot, same shape as Vercel).\n *\n * `launchDockerd: false` because E2B microVMs can't run nested containers.\n */\n\nimport type { BoxRecord, Provider, ProviderCheckpoint } from '@agentbox/core';\nimport {\n createCloudProvider,\n currentCloudBaseFingerprint,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from '@agentbox/sandbox-cloud';\nimport { readCliStamp, recordBox } from '@agentbox/sandbox-core';\nimport { e2bBackend, DEFAULT_BOX_IMAGE_REF } from './backend.js';\nimport { Sandbox, resolveApiKey } from './sdk.js';\nimport { withE2bRetry } from './retry.js';\nimport { prepareE2bProvider } from './prepare.js';\nimport { buildE2bAttach } from './build-attach.js';\nimport { currentE2bBaseFingerprintLive } from './prepared-state.js';\n\nconst BACKEND_NAME = 'e2b';\n\nconst cloudProvider = createCloudProvider(e2bBackend, {\n // E2B applies resources at the template level (Template.build({ cpuCount,\n // memoryMB }) — `prepare` sets these). The numbers below are advisory\n // metadata for BoxRecord stats / the dashboard pane; per-create overrides\n // aren't honored by the SDK.\n defaultResources: { cpu: 2, memory: 4, disk: 8 },\n launchDockerd: false,\n});\n\n/**\n * Create a reusable, named E2B snapshot from a running sandbox.\n * `Sandbox.createSnapshot` pauses the source while capturing, then returns a\n * persistent `snapshotId` (template id form: `name:tag` or `template-id:tag`)\n * usable with `Sandbox.create({ template })` — see SDK docs.\n */\nasync function createE2bSnapshot(sandboxId: string, name: string): Promise<string> {\n const apiKey = resolveApiKey();\n return withE2bRetry(\n { method: 'createSnapshot', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const info = await Sandbox.createSnapshot(sandboxId, { apiKey, name });\n return info.snapshotId;\n },\n );\n}\n\n/** Delete a snapshot by id. Idempotent — a missing snapshot is success. */\nasync function deleteE2bSnapshot(snapshotId: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry({ method: 'deleteSnapshot', retryOnAmbiguous: true }, async () => {\n try {\n await Sandbox.deleteSnapshot(snapshotId, { apiKey });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/not.?found|404/i.test(msg)) return; // idempotent\n throw err;\n }\n });\n}\n\n/**\n * Build a safe, unique E2B snapshot name. E2B names are global within a team\n * (`createSnapshot({ name })` reuses an existing name if it exists, growing\n * the build set). We stamp a per-create timestamp so each checkpoint gets its\n * own name → its own template id, never overwriting an earlier checkpoint.\n */\nfunction snapshotName(boxName: string, checkpointName: string): string {\n // E2B template names accept lower-case alnum + dashes/dots; coerce.\n const sanitize = (s: string): string =>\n s.toLowerCase().replace(/[^a-z0-9.-]+/g, '-').replace(/^-+|-+$/g, '');\n const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').replace('Z', '');\n return `agentbox-${sanitize(boxName)}-${sanitize(checkpointName)}-${sanitize(ts)}`;\n}\n\n/**\n * E2B-specific checkpoint capability. Unlike the scaffold's default (which\n * stores a caller-chosen snapshot *name*), we capture the SDK-returned\n * opaque snapshot id and store THAT in the manifest's `snapshotName` field —\n * the cloud create flow passes `manifest.snapshotName` straight to\n * `provision({ snapshot })`, and the E2B backend boots from it as a template\n * id. (Same id-addressed shape as Vercel's checkpoint capability.)\n */\nconst e2bCheckpoint: ProviderCheckpoint = {\n async create(box: BoxRecord, name: string) {\n if (!box.projectRoot) {\n throw new Error(\n 'cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)',\n );\n }\n if (!box.cloud?.sandboxId) {\n throw new Error(`e2b box ${box.name} has no sandboxId — record is malformed`);\n }\n // NOTE: createSnapshot pauses the source sandbox; agentbox-ctl-checkpoint\n // resumes it lazily on the next op (Sandbox.connect auto-resumes).\n const e2bSnapName = snapshotName(box.name, name);\n const snapshotId = await createE2bSnapshot(box.cloud.sandboxId, e2bSnapName);\n // The box is now paused — persist it so the fast `agentbox list` path\n // doesn't show a stale `running` after a checkpoint. Best-effort.\n try {\n await recordBox({ ...box, cloud: { ...box.cloud, lastState: 'paused' } });\n } catch {\n // not worth failing the checkpoint over a state-record write\n }\n const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {\n snapshotName: snapshotId,\n sourceBoxId: box.id,\n sourceBoxName: box.name,\n baseProvider: BACKEND_NAME,\n baseFingerprint: currentCloudBaseFingerprint(BACKEND_NAME),\n cliVersion: readCliStamp().cliVersion,\n });\n return { ref: info.name };\n },\n async list(projectRoot: string) {\n const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);\n return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));\n },\n async remove(projectRoot: string, ref: string) {\n const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);\n if (!entry) return;\n try {\n await deleteE2bSnapshot(entry.manifest.snapshotName);\n } catch {\n // best-effort: drop the local manifest even if the remote delete failed\n // (network/perms/already-gone) so the user isn't left with a dead pointer.\n }\n await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);\n },\n};\n\nexport const e2bProvider: Provider = {\n ...cloudProvider,\n prepare: prepareE2bProvider,\n buildAttach: buildE2bAttach,\n checkpoint: e2bCheckpoint,\n baseFingerprint: () => currentE2bBaseFingerprintLive(),\n};\n\nexport { e2bBackend, DEFAULT_BOX_IMAGE_REF };\nexport { ensureE2bEnvLoaded, reloadE2bEnv } from './env-loader.js';\nexport {\n ensureE2bCredentials,\n readE2bCredStatus,\n secretsPath,\n maskKey,\n type EnsureE2bCredentialsOptions,\n type E2bCredStatus,\n} from './credentials.js';\nexport {\n RUNTIME_ASSETS,\n candidatesFor,\n resolveRuntimeAssets,\n findStagedCliRuntimeRoot,\n type RuntimeAsset,\n type ResolvedAsset,\n} from './runtime-assets.js';\nexport {\n prepareE2b,\n prepareE2bProvider,\n type PrepareE2bOptions,\n type PrepareE2bResult,\n} from './prepare.js';\nexport {\n currentE2bBaseFingerprintLive,\n ensureE2bBaseTemplate,\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n updatePreparedState,\n type PreparedE2bState,\n type PreparedE2bBase,\n} from './prepared-state.js';\nexport { buildE2bAttach } from './build-attach.js';\n","/**\n * E2B `CloudBackend` — maps the provider-neutral cloud primitives onto the\n * `e2b` v2 SDK (Firecracker microVMs + pause/resume persistence). Composed\n * into a full `Provider` by `@agentbox/sandbox-cloud`'s `createCloudProvider`.\n *\n * Platform shape this backend is built around:\n * - Boxes boot from the prepared base template baked by `agentbox prepare\n * --provider e2b` (Template.build → custom Debian image with agentbox-ctl,\n * the vscode user, /workspace, claude/codex/opencode, tmux, Chromium).\n * `backend.provision` gates on `ensureE2bBaseTemplate()` (mirrors the\n * hetzner/vercel pattern: `prepare` itself sidesteps the gate so a cold\n * install can bootstrap). A snapshot ref (cloud checkpoint) wins over\n * the prepared base.\n * - No nested containers (Firecracker microVM); the provider sets\n * `launchDockerd: false`.\n * - Preview URLs (`{port}-{sandboxId}.{E2B_DOMAIN}`) are public HTTPS by\n * default (allowPublicTraffic=true); no header token needed. We construct\n * the URL string locally so `previewUrl` doesn't have to `Sandbox.connect`\n * (which would auto-resume a paused box).\n * - `Sandbox.getInfo` is a NON-resuming static API; `state()`/`get()` use it\n * to check existence cheaply without waking a paused sandbox. Auto-resume\n * happens only inside `Sandbox.connect` (used by ops that need a live\n * handle: exec, file ops, pause, destroy).\n * - `Sandbox.pause` is the canonical pause API (`betaPause` is deprecated).\n * - `Sandbox.createSnapshot` is the reusable-snapshot primitive; the\n * provider overrides the whole `checkpoint` capability in index.ts to\n * store the resulting snapshot id (matching vercel's id-addressed shape).\n * - No SSH — the provider overrides `buildAttach` with an SDK-streaming\n * PTY bridge (`buildE2bAttach`); the legacy `attachArgv` slot stays\n * unset.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\nimport type { SandboxInfo, SandboxState } from './sdk.js';\nimport { Sandbox, Template, resolveApiKey } from './sdk.js';\nimport { withE2bRetry } from './retry.js';\nimport { ensureE2bBaseTemplate, readPreparedState } from './prepared-state.js';\n\n/**\n * Sentinel image ref the cloud-provider hands us when no --image was passed.\n * Mirrors docker's convention; the actual template id is read from the\n * prepared-state file by `provision`.\n */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\n/** Box user agentbox standardizes on (matches docker/vercel — created by build-template.sh). */\nconst BOX_USER = 'vscode';\nconst BOX_OWNER = 'vscode:vscode';\n\n/** Default E2B preview hostname. Override via the SDK's `E2B_DOMAIN` env. */\nconst DEFAULT_E2B_DOMAIN = 'e2b.app';\n\n/**\n * Per-box session timeout the SDK enforces. Past it, E2B auto-terminates the\n * sandbox; we explicitly extend via `sb.setTimeout` is not needed for Task 1's\n * smoke (boxes survive minutes, not hours). 45 min default mirrors vercel.\n */\nconst DEFAULT_TIMEOUT_MS = 45 * 60_000;\n\nconst E2B_WEB_PORT = 8080;\n\n/** Single-quote a string for safe embedding inside a `bash -lc '<…>'`. */\nfunction shq(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/**\n * Convert a Node `Buffer` to a plain `ArrayBuffer` because E2B's `files.write`\n * `data:` field is `string | ArrayBuffer | Blob | ReadableStream` — Buffer is a\n * `Uint8Array` subclass and doesn't satisfy that union at the type level (even\n * though it works at runtime). Copy rather than slice the underlying buffer:\n * Buffers may share an underlying ArrayBuffer with a pooled allocator, so\n * `data.buffer` of a small Buffer can be a megabyte-long shared region.\n */\nfunction bufferToArrayBuffer(b: Buffer): ArrayBuffer {\n const ab = new ArrayBuffer(b.byteLength);\n new Uint8Array(ab).set(b);\n return ab;\n}\n\n/**\n * Map the SDK's nullable string state onto our 4-value `CloudState`.\n * E2B reports 'running' | 'paused' (per SDK types). Anything else (or absent)\n * → 'missing' so callers ping-pong the lifecycle into a clean state.\n */\nfunction mapState(s: SandboxState | undefined): CloudState {\n switch (s) {\n case 'running':\n return 'running';\n case 'paused':\n return 'paused';\n default:\n return 'missing';\n }\n}\n\n/** True when the error means \"sandbox doesn't exist\" (404). */\nfunction isNotFound(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const name = err instanceof Error ? err.name : '';\n if (name === 'SandboxNotFoundError' || name === 'NotFoundError') return true;\n const status = (err as { statusCode?: unknown; status?: unknown }).statusCode ?? (err as { status?: unknown }).status;\n return status === 404;\n}\n\n/**\n * Sanitize a box name for use as the `metadata.name` value. E2B accepts\n * arbitrary strings (probed) but we strip control chars defensively so a name\n * with embedded newlines can't break log parsing or response shapes.\n */\nfunction safeMetadataName(name: string): string {\n return name.replace(/[\\u0000-\\u001f]/g, '').slice(0, 200);\n}\n\nexport const e2bBackend: CloudBackend = {\n name: 'e2b',\n\n // The cloud scaffold's WebProxy binds whatever port we expose here, and\n // `agentbox url --kind=web` resolves via `getHost(port)`. 8080 matches the\n // non-privileged convention vercel uses — `getHost` accepts any port, but\n // staying on 8080 keeps the in-box ctl flag (AGENTBOX_WEB_PROXY_PORT)\n // identical across cloud providers.\n webProxyPort: E2B_WEB_PORT,\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n const apiKey = resolveApiKey();\n const log = req.onLog ?? (() => {});\n // Resolve the template to boot from: an explicit cloud-checkpoint snapshot\n // (req.snapshot) wins, else the prepared base template id. We don't fall\n // back to E2B's stock `base` template — every box must have the agentbox\n // runtime baked in. The gate throws an actionable \"run `agentbox prepare`\"\n // error when no template is recorded yet (mirrors the hetzner/vercel\n // pattern: `prepare` itself sidesteps the gate by calling `prepareE2b`\n // directly, never `provision`).\n if (req.snapshot === undefined) {\n ensureE2bBaseTemplate();\n }\n const template = req.snapshot ?? readPreparedState().base?.templateId;\n if (!template) {\n throw new Error(\n 'e2b provision: no template available — `agentbox prepare --provider e2b` must run first',\n );\n }\n\n // No-retry: Sandbox.create is billable and non-idempotent — a timeout\n // after the request reached the origin could leave a duplicate sandbox we\n // can't reference for cleanup.\n const sb = await withE2bRetry(\n { method: 'provision', retryOnAmbiguous: false, attemptTimeoutMs: 300_000, backoffMs: [] },\n async () =>\n Sandbox.create({\n apiKey,\n template,\n // Friendly name (so prune can see it) + the 'agentbox' marker so\n // `list()` can filter out sandboxes provisioned by other tooling.\n metadata: { agentbox: 'true', 'agentbox.name': safeMetadataName(req.name), name: safeMetadataName(req.name) },\n envs: req.env,\n timeoutMs: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n }),\n );\n log(`e2b: created sandbox ${sb.sandboxId} (template ${template})`);\n return { sandboxId: sb.sandboxId };\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'get', retryOnAmbiguous: true }, async () => {\n try {\n // Static, NON-resuming — won't wake a paused sandbox just to confirm\n // it exists (per orchestrator review #1: connect would auto-resume).\n await Sandbox.getInfo(sandboxId, { apiKey });\n return { sandboxId };\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'list', retryOnAmbiguous: true }, async () => {\n const summaries: CloudSandboxSummary[] = [];\n // Default query returns both running and paused sandboxes. We filter\n // client-side to the ones we created (metadata.agentbox === 'true').\n for (const state of ['running', 'paused'] as const) {\n const paginator = Sandbox.list({ apiKey, query: { state: [state] } });\n while (paginator.hasNext) {\n const page = await paginator.nextItems();\n for (const info of page) {\n if (info.metadata?.['agentbox'] !== 'true') continue;\n const friendly =\n info.metadata?.['agentbox.name'] ?? info.metadata?.['name'];\n const summary: CloudSandboxSummary = { sandboxId: info.sandboxId, state };\n if (friendly) summary.name = friendly;\n const startedAt = info.startedAt;\n if (startedAt instanceof Date) summary.createdAt = startedAt.toISOString();\n summaries.push(summary);\n }\n }\n }\n return summaries;\n });\n },\n\n // E2B has no separate stop primitive — sandboxes are either running or\n // paused. start is therefore a connect-and-resume (auto-resume inside\n // Sandbox.connect handles a paused box transparently).\n async start(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'start', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n await Sandbox.connect(h.sandboxId, { apiKey });\n },\n );\n },\n\n // stop ≡ pause on E2B (the pause IS the cold-storage state).\n async stop(h: CloudHandle): Promise<void> {\n await this.pause(h);\n },\n\n async pause(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'pause', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n await Sandbox.pause(h.sandboxId, { apiKey });\n },\n );\n },\n\n async resume(h: CloudHandle): Promise<void> {\n await this.start(h);\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'destroy', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n try {\n await Sandbox.kill(h.sandboxId, { apiKey });\n } catch (err) {\n if (isNotFound(err)) return; // idempotent\n throw err;\n }\n },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'state', retryOnAmbiguous: true }, async () => {\n try {\n const info: SandboxInfo = await Sandbox.getInfo(h.sandboxId, { apiKey });\n return mapState(info.state);\n } catch (err) {\n if (isNotFound(err)) return 'missing';\n throw err;\n }\n });\n },\n\n async exec(h: CloudHandle, cmd: string, opts?: CloudExecOptions): Promise<CloudExecResult> {\n const apiKey = resolveApiKey();\n // Default per-attempt cap is 5 min — covers the cloud scaffold's\n // workspace-seed/carry extracts (tar of thousands of files, chown -R).\n // Callers can shorten with opts.attemptTimeoutMs for snappier probes.\n const timeoutMs = opts?.attemptTimeoutMs ?? 300_000;\n return withE2bRetry(\n {\n method: 'exec',\n retryOnAmbiguous: opts?.noRetry ? false : true,\n attemptTimeoutMs: timeoutMs,\n backoffMs: opts?.noRetry ? [] : undefined,\n },\n async () => {\n // Connect for the live handle — auto-resumes a paused box, which is\n // the correct semantics for exec (caller wants the command to run).\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n // E2B's `commands.run` accepts only 'root' | 'user' | 'vscode'…; any\n // unix username we create in the fixup is valid. Pass through.\n const user = (opts?.user ?? BOX_USER) as 'root' | 'user';\n try {\n const r = await sb.commands.run(cmd, {\n ...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts?.env !== undefined ? { envs: opts.env } : {}),\n user,\n timeoutMs,\n });\n return { exitCode: r.exitCode, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };\n } catch (err) {\n // commands.run throws on non-zero exit; the CommandResult fields\n // (exitCode/stdout/stderr) hang off the error. Map back into our\n // CloudExecResult so callers see exit=1, not a thrown exception\n // (vercel/daytona/hetzner exec contract returns the result).\n if (err instanceof Error && err.name === 'CommandExitError') {\n const ce = err as unknown as {\n exitCode: number;\n stdout: string;\n stderr: string;\n };\n return { exitCode: ce.exitCode, stdout: ce.stdout ?? '', stderr: ce.stderr ?? '' };\n }\n throw err;\n }\n },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'uploadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const data = await readFile(localPath);\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n await sb.files.write([{ path: remotePath, data: bufferToArrayBuffer(data) }]);\n // files.write writes as the default user; chown to vscode so reads\n // from the scaffold's `sudo -u vscode …` exec calls succeed. Best-\n // effort — a chown failure on a world-readable file is harmless.\n try {\n await sb.commands.run(`sudo -n chown ${BOX_OWNER} ${shq(remotePath)}`, {\n user: 'root',\n timeoutMs: 10_000,\n });\n } catch {\n // ignore — file is at least present and readable\n }\n },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'downloadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n const bytes = await sb.files.read(remotePath, { format: 'bytes' });\n const { writeFile } = await import('node:fs/promises');\n await writeFile(localPath, Buffer.from(bytes));\n },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'listFiles', retryOnAmbiguous: true }, async () => {\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n const entries = await sb.files.list(remoteDir);\n return entries.map((e) => ({ name: e.name, isDir: e.type === 'dir' }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n // E2B's `sandbox.getHost(port)` is just string interpolation of\n // `${port}-${sandboxId}.${sandboxDomain}` — calling it via Sandbox.connect\n // would auto-resume a paused box (the SDK's documented behavior). The\n // domain defaults to `e2b.app` with `E2B_DOMAIN` as the override (matches\n // the SDK's `ConnectionConfig` default), so we construct the URL locally\n // and never wake the box for a UI/dashboard URL fetch.\n const domain = process.env.E2B_DOMAIN ?? DEFAULT_E2B_DOMAIN;\n return { url: `https://${String(port)}-${h.sandboxId}.${domain}`, token: undefined };\n },\n\n // Fewer params than the interface's (h, port, expiresInSeconds) is fine —\n // E2B preview URLs are already public + browser-usable; no per-URL TTL.\n async signedPreviewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return this.previewUrl(h, port);\n },\n\n /**\n * Probe whether a snapshot (i.e. a template id) is still bootable. E2B's\n * snapshot ids look like `template-id:tag` or `team-slug/name:tag` — both\n * accepted by `Template.exists(name)`. Returns false on any lookup failure\n * (treated by the cloud-provider as \"gone\" so it falls back to a from-base\n * boot rather than 410ing the user).\n */\n async snapshotExists(snapshotName: string): Promise<boolean> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'snapshotExists', retryOnAmbiguous: true }, async () => {\n try {\n return await Template.exists(snapshotName, { apiKey });\n } catch {\n return false;\n }\n });\n },\n};\n","/**\n * Bounded retry wrapper for E2B SDK calls — mirrors `withVercelRetry` /\n * `withHetznerRetry` in shape and intent. The E2B API rate-limits (429) and\n * can return transient 5xx during incidents; without bounded retries those\n * propagate as wedges in the calling lifecycle code.\n *\n * Non-idempotent ops (`provision`/`Sandbox.create`) pass `retryOnAmbiguous:\n * false` so a timeout after the request reached the origin doesn't create a\n * duplicate billable sandbox.\n */\n\nexport interface WithRetryOptions {\n method: string;\n /** Per-attempt timeout (ms). Default 30_000. */\n attemptTimeoutMs?: number;\n /** Backoff before attempts 2, 3, … (ms). Default [1000, 2000, 4000]. */\n backoffMs?: readonly number[];\n /**\n * Retry on errors where we can't be sure the server applied the request\n * (connection failures, per-attempt timeouts, 5xx). Set false for\n * non-idempotent operations where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default stderr retry sink (used by tests). */\n onRetry?: (line: string) => void;\n}\n\nconst DEFAULT_BACKOFF: readonly number[] = [1000, 2000, 4000];\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 30_000;\n\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`e2b ${method}: per-attempt timeout after ${String(ms)}ms`);\n this.name = 'AttemptTimeoutError';\n }\n}\n\nexport function isAttemptTimeout(err: unknown): err is AttemptTimeoutError {\n return err instanceof AttemptTimeoutError;\n}\n\n/** HTTP status code dug out of whatever error shape the SDK throws. */\nfunction statusCodeOf(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n for (const key of ['statusCode', 'status', 'code'] as const) {\n const v = (err as Record<string, unknown>)[key];\n if (typeof v === 'number') return v;\n }\n const resp = (err as { response?: { status?: unknown } }).response;\n if (resp && typeof resp.status === 'number') return resp.status;\n return undefined;\n}\n\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n // The SDK exposes a dedicated `SandboxNotFoundError` and `RateLimitError`.\n // Match by error name to avoid an import cycle (sdk.ts → retry → sdk).\n const name = err instanceof Error ? err.name : undefined;\n if (name === 'RateLimitError') return true;\n if (name === 'SandboxNotFoundError') return false;\n\n const status = statusCodeOf(err);\n if (status !== undefined) {\n if (status === 429) return true;\n if (status >= 500 && status <= 599) return allowAmbiguous;\n return false;\n }\n\n // Raw fetch / undici errors. Node wraps low-level errors in `{ cause }`.\n if (err && typeof err === 'object') {\n const candidates: unknown[] = [err, (err as { cause?: unknown }).cause];\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue;\n const code = (c as { code?: unknown }).code;\n if (\n code === 'ECONNRESET' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNABORTED' ||\n code === 'EAI_AGAIN' ||\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'UND_ERR_SOCKET' ||\n code === 'UND_ERR_CONNECT_TIMEOUT'\n ) {\n return allowAmbiguous;\n }\n }\n }\n return false;\n}\n\nexport async function withE2bRetry<T>(opts: WithRetryOptions, fn: () => Promise<T>): Promise<T> {\n const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;\n const maxAttempts = backoff.length + 1;\n const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const log = opts.onRetry ?? defaultRetryLog;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await raceTimeout(fn(), timeoutMs, opts.method);\n } catch (err) {\n const last = attempt === maxAttempts;\n if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;\n const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4000;\n log(\n `e2b ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withE2bRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[e2b-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number, method: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n p,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\nfunction errorSummary(err: unknown): string {\n if (err instanceof Error) {\n const status = statusCodeOf(err);\n return status !== undefined\n ? `${err.name}(${String(status)}): ${truncate(err.message)}`\n : `${err.name}: ${truncate(err.message)}`;\n }\n return truncate(String(err));\n}\n\nfunction truncate(s: string, max = 160): string {\n return s.length > max ? `${s.slice(0, max)}…` : s;\n}\n","/**\n * `agentbox prepare --provider e2b` — bake the E2B base template.\n *\n * Unlike Vercel/Hetzner, E2B can build templates from a build DSL (the SDK's\n * `Template` + `Template.build`). We mirror Vercel's `prepare` shape but drive\n * the build through the SDK builder API instead of booting a sandbox + running\n * `provision.sh`:\n *\n * 1. Resolve runtime assets + fingerprint the build context. Skip-fast when\n * an up-to-date template id is already recorded.\n * 2. Stage every resolved asset under a temp `fileContextPath` directory\n * with predictable relative names (E2B's `template.copy(src, dest)`\n * requires sources to be RELATIVE paths inside the context dir).\n * 3. `Template({ fileContextPath })` → `.fromBaseImage()` (E2B's default\n * Debian 12 + node 20 + git + sudo). `.copy(rel, dest)` for each asset,\n * `.runCmd('bash /tmp/agentbox-build-template.sh', { user: 'root' })`,\n * `.setReadyCmd('test -x /usr/local/bin/agentbox-ctl')`.\n * 4. `Template.build(t, 'agentbox-base:<tag>', { cpuCount, memoryMB,\n * onBuildLogs })` streams logs into the spinner; returns the BuildInfo\n * with the template id.\n * 5. Persist `{ schema:1, base: { templateId, contextSha256, cliVersion,\n * cliCommit, createdAt } }` to ~/.agentbox/e2b-prepared.json.\n *\n * Templates on E2B are reusable named resources addressed by id+tag. Re-running\n * with the same name reuses the existing template id (E2B's documented\n * behavior). Unlike Vercel snapshots there's no per-box eviction concern; one\n * template is reused for every create.\n *\n * vCPU / RAM are template-level on E2B — set them here so per-box `create`\n * doesn't try to override them (which E2B rejects).\n */\n\nimport { copyFile, mkdir, mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport type { Provider } from '@agentbox/core';\nimport { computeContextSha256, readCliStamp } from '@agentbox/sandbox-core';\nimport { ensureE2bCredentials } from './credentials.js';\nimport { resolveApiKey, Template } from './sdk.js';\nimport {\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n} from './prepared-state.js';\nimport {\n findStagedCliRuntimeRoot,\n resolveRuntimeAssets,\n type ResolvedAsset,\n} from './runtime-assets.js';\n\nexport interface PrepareE2bOptions {\n name?: string;\n hostWorkspace?: string;\n /** Force re-bake even when an up-to-date template id is recorded. */\n force?: boolean;\n /** vCPUs for the baked template (default 2). E2B applies this per-sandbox at boot. */\n cpuCount?: number;\n /** Memory in MiB for the baked template (default 4096). */\n memoryMB?: number;\n /** CLI runtime tree (set by the CLI to its dist neighbor). */\n cliRuntimeRoot?: string;\n /** Repo root for the dev fallback (defaults to a cwd-walk). */\n repoRoot?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface PrepareE2bResult {\n snapshotName?: string;\n}\n\n/** Template name agentbox bakes under. E2B treats `name:tag` as a single addressable build. */\nconst TEMPLATE_NAME = 'agentbox-base:latest';\nconst DEFAULT_TAG = 'latest';\n\nconst DEFAULT_CPU = 2;\nconst DEFAULT_MEMORY_MB = 4096;\n\nexport async function prepareE2b(\n opts: PrepareE2bOptions = {},\n): Promise<PrepareE2bResult> {\n await ensureE2bCredentials();\n const apiKey = resolveApiKey();\n const log = opts.onLog ?? (() => {});\n const progress = (s: string) => log(`prepare-e2b: ${s}`);\n\n const assets = resolveRuntimeAssets({\n cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),\n repoRoot: opts.repoRoot,\n });\n const contextSha = await computeContextSha256(\n assets.map((a) => ({ rel: a.name, abs: a.localPath })),\n );\n\n // Skip-fast: existing template + matching fingerprint.\n //\n // Probe the persisted templateId itself, not TEMPLATE_NAME. If someone\n // (or another bake) rebuilt the alias under a different id, the name still\n // resolves but the stored id is stale and `Sandbox.create({ template: <stale id> })`\n // 404s. `Template.exists` accepts both `name:tag` and `template-id:tag`\n // forms, so we pass the exact id we'd later hand to `provision`.\n const existing = readPreparedState();\n if (!opts.force && existing.base) {\n if (existing.base.contextSha256 === contextSha) {\n const stillThere = await templateExists(existing.base.templateId, apiKey);\n if (stillThere) {\n progress(\n `template ${existing.base.templateId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`,\n );\n return { snapshotName: existing.base.templateId };\n }\n progress(`recorded template ${existing.base.templateId} is gone on E2B; rebuilding`);\n } else {\n progress(\n `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? '<none>'}, now ${contextSha.slice(0, 12)}); rebuilding`,\n );\n }\n }\n\n // E2B's `template.copy(src, dest)` requires `src` to be a RELATIVE path\n // inside the Template's `fileContextPath`. Stage every resolved asset into\n // a temp dir under its logical name (asset.name) so the copy chain reads\n // from a single context root.\n const contextDir = await mkdtemp(join(tmpdir(), 'agentbox-e2b-build-'));\n try {\n progress(`staging build context at ${contextDir}`);\n await stageAssetsInto(contextDir, assets);\n\n // Build the Template via the SDK builder. fromBaseImage() starts from E2B's\n // own `e2bdev/base` (Debian 12 + node 20 + git + sudo), which halves the\n // install time vs starting from a vanilla Debian image.\n progress('assembling template build (fromBaseImage + asset copy + runCmd)');\n const template = Template({ fileContextPath: contextDir }).fromBaseImage();\n for (const a of assets) {\n progress(` copy ${a.name} -> ${a.remotePath}`);\n template.copy(a.name, a.remotePath, {\n forceUpload: true,\n mode: a.remoteMode,\n user: 'root',\n });\n }\n template.runCmd('bash /tmp/agentbox-build-template.sh 2>&1', { user: 'root' });\n // setReadyCmd flips the builder into TemplateFinal — required for build().\n // The check passes once the script's last `install` step lands the ctl bundle.\n const finalTemplate = template.setReadyCmd(\n 'test -x /usr/local/bin/agentbox-ctl',\n );\n\n const cpuCount = opts.cpuCount ?? DEFAULT_CPU;\n const memoryMB = opts.memoryMB ?? DEFAULT_MEMORY_MB;\n progress(\n `running Template.build('${TEMPLATE_NAME}', { cpuCount: ${String(cpuCount)}, memoryMB: ${String(memoryMB)} })`,\n );\n const info = await Template.build(finalTemplate, TEMPLATE_NAME, {\n apiKey,\n cpuCount,\n memoryMB,\n onBuildLogs: (entry: LogEntryLike) => {\n // LogEntry exposes timestamp / level / message; stream the human form.\n log(`[build] ${formatBuildLog(entry)}`);\n },\n });\n progress(`template built: id=${info.templateId} build=${info.buildId} name=${info.name}`);\n\n // Persist. `Sandbox.create({ template })` auto-appends `:default` when no\n // tag is given (and 404s if that tag wasn't built), so we MUST store the\n // tagged form. `info.templateId` is just the raw id with no tag; use the\n // first tag we built with (`latest`) or fall back to `info.tags[0]`.\n const tag = info.tags?.[0] ?? DEFAULT_TAG;\n const cliStamp = readCliStamp();\n const taggedId = `${info.templateId}:${tag}`;\n writePreparedState({\n schema: 1,\n base: {\n templateId: taggedId,\n // info.name is the full `name:tag` pair Template.build() was called\n // with (e.g. `agentbox-base:latest`). Earlier code re-appended `:${tag}`\n // and produced `agentbox-base:latest:latest` in the status display.\n templateName: info.name,\n contextSha256: contextSha,\n cliVersion: cliStamp.cliVersion,\n cliCommit: cliStamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n });\n progress(`wrote ${preparedStatePath()}`);\n\n progress(`prepare complete — base template ${taggedId}`);\n return { snapshotName: taggedId };\n } finally {\n await rm(contextDir, { recursive: true, force: true }).catch(() => {\n // best-effort: temp dir cleanup failures are noise, not errors.\n });\n }\n}\n\n/**\n * Copy every asset into `contextDir` under its logical `name`. Preserves the\n * source mode on the copy (E2B's `template.copy` also accepts a `mode`\n * override, but the on-disk mode keeps the local stage representative).\n */\nasync function stageAssetsInto(\n contextDir: string,\n assets: ResolvedAsset[],\n): Promise<void> {\n for (const a of assets) {\n const dest = resolve(contextDir, a.name);\n await mkdir(dirname(dest), { recursive: true });\n await copyFile(a.localPath, dest);\n }\n}\n\n/**\n * Check if a named template is bootable on E2B. Returns true on a 'ready'\n * build, false on anything else (deleted, errored, never built). Used by the\n * skip-fast path to detect a template that was deleted out-of-band.\n */\nasync function templateExists(name: string, apiKey: string): Promise<boolean> {\n try {\n return await Template.exists(name, { apiKey });\n } catch {\n return false;\n }\n}\n\n/**\n * E2B's `LogEntry` shape (timestamp, level, message). We treat the SDK's\n * type loosely here so the line-stream doesn't bind us to internal class\n * shapes — only the `.toString()` plus `.message` are documented.\n */\ninterface LogEntryLike {\n message?: string;\n level?: string;\n timestamp?: Date;\n toString(): string;\n}\n\nfunction formatBuildLog(entry: LogEntryLike): string {\n // The SDK's LogEntry.toString() emits a `[level] timestamp message` form.\n // For the spinner we only want the message — and clip overly long lines.\n const raw = typeof entry.message === 'string' ? entry.message : entry.toString();\n const cleaned = raw.replace(/\\r?\\n+$/, '');\n return cleaned.length > 200 ? cleaned.slice(0, 200) + '…' : cleaned;\n}\n\n/** Provider-level binding used by the CLI's `prepare` command. */\nexport const prepareE2bProvider: NonNullable<Provider['prepare']> = (req) =>\n prepareE2b({\n name: req.name,\n hostWorkspace: req.hostWorkspace ?? process.cwd(),\n force: req.force,\n onLog: req.onLog,\n });\n","/**\n * `buildE2bAttach` — the E2B provider's override of `Provider.buildAttach`.\n *\n * E2B has no SSH and no public PTY CLI like Vercel's `sbx exec -i`. We ship\n * our own attach helper (`attach-helper.cjs`) that connects to the sandbox via\n * the SDK and bridges the host PTY to an in-box `sandbox.pty.create()`.\n *\n * Argv shape:\n * node <attach-helper.cjs> --sandbox-id <id> --user vscode\n *\n * The inner bash command (`renderInnerCommand` output: tmux ensure + attach)\n * is passed through the environment as `AGENTBOX_E2B_INNER_CMD` so quoting\n * stays sane and it doesn't leak through `ps`. `E2B_API_KEY` is passed through\n * the env for the same reason. `node-pty` (the host PTY wrapper) is what\n * spawns this argv, so stdin/stdout/SIGWINCH all reach the helper unchanged.\n */\n\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AttachKind,\n type AttachSpec,\n type BoxRecord,\n type BuildAttachOptions,\n} from '@agentbox/core';\nimport { renderInnerCommand } from '@agentbox/sandbox-cloud';\nimport { resolveApiKey } from './sdk.js';\n\nconst SELF = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the absolute path to `attach-helper.cjs`. In the published CLI it\n * lives at `runtime/e2b/attach-helper.cjs` (next to the staged provider\n * runtime tree); in dev it lives next to the package's `dist/`.\n */\nexport function resolveAttachHelperPath(): string {\n const candidates = [\n // dev: dist/index.js sibling\n resolve(SELF, 'attach-helper.cjs'),\n // dev: src compiled to dist/, while index.ts is in src/\n resolve(SELF, '..', 'dist', 'attach-helper.cjs'),\n // staged CLI: apps/cli/runtime/e2b/attach-helper.cjs\n resolve(SELF, '..', 'runtime', 'e2b', 'attach-helper.cjs'),\n resolve(SELF, '..', '..', 'runtime', 'e2b', 'attach-helper.cjs'),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n // Last-resort: still return the first candidate so the error message points\n // somewhere informative.\n return candidates[0]!;\n}\n\nexport async function buildE2bAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n): Promise<AttachSpec> {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`e2b box ${box.name} has no sandboxId — record is malformed`);\n }\n\n const helper = resolveAttachHelperPath();\n if (!existsSync(helper)) {\n throw new Error(\n `e2b attach helper not found at ${helper} — rebuild the CLI (\\`pnpm -w build\\`) ` +\n 'so packages/sandbox-e2b/dist/attach-helper.cjs is generated.',\n );\n }\n\n const apiKey = resolveApiKey();\n const inner = renderInnerCommand(kind, opts);\n\n const argv = [\n process.execPath,\n helper,\n '--sandbox-id',\n sandboxId,\n '--user',\n 'vscode',\n ];\n\n return {\n argv,\n env: {\n E2B_API_KEY: apiKey,\n AGENTBOX_E2B_INNER_CMD: inner,\n },\n };\n}\n"],"mappings":"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","names":["dirname","resolve","snapshotName","dirname","resolve","readCliStamp"]}
|
|
1
|
+
{"version":3,"sources":["../../../packages/sandbox-e2b/src/index.ts","../../../packages/sandbox-e2b/src/backend.ts","../../../packages/sandbox-e2b/src/retry.ts","../../../packages/sandbox-e2b/src/prepare.ts","../../../packages/sandbox-e2b/src/build-attach.ts"],"sourcesContent":["/**\n * The E2B sandbox provider. A thin `CloudBackend` over the `e2b` v2 SDK,\n * composed via `@agentbox/sandbox-cloud`'s `createCloudProvider` for\n * everything provider-agnostic (workspace seeding, ctl launch, state, relay\n * polling).\n *\n * Three capabilities are overridden on top of the cloud scaffold:\n * - `prepare` — bake the custom base template via Template.build\n * (`agentbox prepare --provider e2b`).\n * - `buildAttach` — SDK-streaming PTY bridge (E2B has no SSH).\n * - `checkpoint` — store the E2B snapshot id (template id) in the manifest\n * so restore boots from it (`Sandbox.createSnapshot` produces an\n * id-addressed reusable snapshot, same shape as Vercel).\n *\n * `launchDockerd: false` because E2B microVMs can't run nested containers.\n */\n\nimport type { BoxRecord, Provider, ProviderCheckpoint } from '@agentbox/core';\nimport {\n createCloudProvider,\n currentCloudBaseFingerprint,\n listCloudCheckpoints,\n removeCloudCheckpointDir,\n resolveCloudCheckpoint,\n writeCloudCheckpointManifest,\n} from '@agentbox/sandbox-cloud';\nimport { readCliStamp, recordBox } from '@agentbox/sandbox-core';\nimport { e2bBackend, DEFAULT_BOX_IMAGE_REF } from './backend.js';\nimport { Sandbox, resolveApiKey } from './sdk.js';\nimport { withE2bRetry } from './retry.js';\nimport { prepareE2bProvider } from './prepare.js';\nimport { buildE2bAttach } from './build-attach.js';\nimport { currentE2bBaseFingerprintLive } from './prepared-state.js';\n\nconst BACKEND_NAME = 'e2b';\n\nconst cloudProvider = createCloudProvider(e2bBackend, {\n // E2B applies resources at the template level (Template.build({ cpuCount,\n // memoryMB }) — `prepare` sets these). The numbers below are advisory\n // metadata for BoxRecord stats / the dashboard pane; per-create overrides\n // aren't honored by the SDK.\n defaultResources: { cpu: 2, memory: 4, disk: 8 },\n launchDockerd: false,\n});\n\n/**\n * Create a reusable, named E2B snapshot from a running sandbox.\n * `Sandbox.createSnapshot` pauses the source while capturing, then returns a\n * persistent `snapshotId` (template id form: `name:tag` or `template-id:tag`)\n * usable with `Sandbox.create({ template })` — see SDK docs.\n */\nasync function createE2bSnapshot(sandboxId: string, name: string): Promise<string> {\n const apiKey = resolveApiKey();\n return withE2bRetry(\n { method: 'createSnapshot', retryOnAmbiguous: false, attemptTimeoutMs: 900_000, backoffMs: [] },\n async () => {\n const info = await Sandbox.createSnapshot(sandboxId, { apiKey, name });\n return info.snapshotId;\n },\n );\n}\n\n/** Delete a snapshot by id. Idempotent — a missing snapshot is success. */\nasync function deleteE2bSnapshot(snapshotId: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry({ method: 'deleteSnapshot', retryOnAmbiguous: true }, async () => {\n try {\n await Sandbox.deleteSnapshot(snapshotId, { apiKey });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/not.?found|404/i.test(msg)) return; // idempotent\n throw err;\n }\n });\n}\n\n/**\n * Build a safe, unique E2B snapshot name. E2B names are global within a team\n * (`createSnapshot({ name })` reuses an existing name if it exists, growing\n * the build set). We stamp a per-create timestamp so each checkpoint gets its\n * own name → its own template id, never overwriting an earlier checkpoint.\n */\nfunction snapshotName(boxName: string, checkpointName: string): string {\n // E2B template names accept lower-case alnum + dashes/dots; coerce.\n const sanitize = (s: string): string =>\n s.toLowerCase().replace(/[^a-z0-9.-]+/g, '-').replace(/^-+|-+$/g, '');\n const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').replace('Z', '');\n return `agentbox-${sanitize(boxName)}-${sanitize(checkpointName)}-${sanitize(ts)}`;\n}\n\n/**\n * E2B-specific checkpoint capability. Unlike the scaffold's default (which\n * stores a caller-chosen snapshot *name*), we capture the SDK-returned\n * opaque snapshot id and store THAT in the manifest's `snapshotName` field —\n * the cloud create flow passes `manifest.snapshotName` straight to\n * `provision({ snapshot })`, and the E2B backend boots from it as a template\n * id. (Same id-addressed shape as Vercel's checkpoint capability.)\n */\nconst e2bCheckpoint: ProviderCheckpoint = {\n async create(box: BoxRecord, name: string) {\n if (!box.projectRoot) {\n throw new Error(\n 'cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)',\n );\n }\n if (!box.cloud?.sandboxId) {\n throw new Error(`e2b box ${box.name} has no sandboxId — record is malformed`);\n }\n // NOTE: createSnapshot pauses the source sandbox; agentbox-ctl-checkpoint\n // resumes it lazily on the next op (Sandbox.connect auto-resumes).\n const e2bSnapName = snapshotName(box.name, name);\n const snapshotId = await createE2bSnapshot(box.cloud.sandboxId, e2bSnapName);\n // The box is now paused — persist it so the fast `agentbox list` path\n // doesn't show a stale `running` after a checkpoint. Best-effort.\n try {\n await recordBox({ ...box, cloud: { ...box.cloud, lastState: 'paused' } });\n } catch {\n // not worth failing the checkpoint over a state-record write\n }\n const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {\n snapshotName: snapshotId,\n sourceBoxId: box.id,\n sourceBoxName: box.name,\n baseProvider: BACKEND_NAME,\n baseFingerprint: currentCloudBaseFingerprint(BACKEND_NAME),\n cliVersion: readCliStamp().cliVersion,\n });\n return { ref: info.name };\n },\n async list(projectRoot: string) {\n const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);\n return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));\n },\n async remove(projectRoot: string, ref: string) {\n const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);\n if (!entry) return;\n try {\n await deleteE2bSnapshot(entry.manifest.snapshotName);\n } catch {\n // best-effort: drop the local manifest even if the remote delete failed\n // (network/perms/already-gone) so the user isn't left with a dead pointer.\n }\n await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);\n },\n};\n\nexport const e2bProvider: Provider = {\n ...cloudProvider,\n prepare: prepareE2bProvider,\n buildAttach: buildE2bAttach,\n checkpoint: e2bCheckpoint,\n baseFingerprint: () => currentE2bBaseFingerprintLive(),\n};\n\nexport { e2bBackend, DEFAULT_BOX_IMAGE_REF };\nexport { ensureE2bEnvLoaded, reloadE2bEnv } from './env-loader.js';\nexport {\n ensureE2bCredentials,\n readE2bCredStatus,\n secretsPath,\n maskKey,\n type EnsureE2bCredentialsOptions,\n type E2bCredStatus,\n} from './credentials.js';\nexport {\n RUNTIME_ASSETS,\n candidatesFor,\n resolveRuntimeAssets,\n findStagedCliRuntimeRoot,\n type RuntimeAsset,\n type ResolvedAsset,\n} from './runtime-assets.js';\nexport {\n prepareE2b,\n prepareE2bProvider,\n type PrepareE2bOptions,\n type PrepareE2bResult,\n} from './prepare.js';\nexport {\n currentE2bBaseFingerprintLive,\n ensureE2bBaseTemplate,\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n updatePreparedState,\n type PreparedE2bState,\n type PreparedE2bBase,\n} from './prepared-state.js';\nexport { buildE2bAttach } from './build-attach.js';\n","/**\n * E2B `CloudBackend` — maps the provider-neutral cloud primitives onto the\n * `e2b` v2 SDK (Firecracker microVMs + pause/resume persistence). Composed\n * into a full `Provider` by `@agentbox/sandbox-cloud`'s `createCloudProvider`.\n *\n * Platform shape this backend is built around:\n * - Boxes boot from the prepared base template baked by `agentbox prepare\n * --provider e2b` (Template.build → custom Debian image with agentbox-ctl,\n * the vscode user, /workspace, claude/codex/opencode, tmux, Chromium).\n * `backend.provision` gates on `ensureE2bBaseTemplate()` (mirrors the\n * hetzner/vercel pattern: `prepare` itself sidesteps the gate so a cold\n * install can bootstrap). A snapshot ref (cloud checkpoint) wins over\n * the prepared base.\n * - No nested containers (Firecracker microVM); the provider sets\n * `launchDockerd: false`.\n * - Preview URLs (`{port}-{sandboxId}.{E2B_DOMAIN}`) are public HTTPS by\n * default (allowPublicTraffic=true); no header token needed. We construct\n * the URL string locally so `previewUrl` doesn't have to `Sandbox.connect`\n * (which would auto-resume a paused box).\n * - `Sandbox.getInfo` is a NON-resuming static API; `state()`/`get()` use it\n * to check existence cheaply without waking a paused sandbox. Auto-resume\n * happens only inside `Sandbox.connect` (used by ops that need a live\n * handle: exec, file ops, pause, destroy).\n * - `Sandbox.pause` is the canonical pause API (`betaPause` is deprecated).\n * - `Sandbox.createSnapshot` is the reusable-snapshot primitive; the\n * provider overrides the whole `checkpoint` capability in index.ts to\n * store the resulting snapshot id (matching vercel's id-addressed shape).\n * - No SSH — the provider overrides `buildAttach` with an SDK-streaming\n * PTY bridge (`buildE2bAttach`); the legacy `attachArgv` slot stays\n * unset.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type {\n CloudBackend,\n CloudExecOptions,\n CloudExecResult,\n CloudFileEntry,\n CloudHandle,\n CloudPreviewUrl,\n CloudProvisionRequest,\n CloudSandboxSummary,\n CloudState,\n} from '@agentbox/core';\nimport type { SandboxInfo, SandboxState } from './sdk.js';\nimport { Sandbox, Template, resolveApiKey } from './sdk.js';\nimport { withE2bRetry } from './retry.js';\nimport { ensureE2bBaseTemplate, readPreparedState } from './prepared-state.js';\n\n/**\n * Sentinel image ref the cloud-provider hands us when no --image was passed.\n * Mirrors docker's convention; the actual template id is read from the\n * prepared-state file by `provision`.\n */\nexport const DEFAULT_BOX_IMAGE_REF = 'agentbox/box:dev';\n\n/** Box user agentbox standardizes on (matches docker/vercel — created by build-template.sh). */\nconst BOX_USER = 'vscode';\nconst BOX_OWNER = 'vscode:vscode';\n\n/** Default E2B preview hostname. Override via the SDK's `E2B_DOMAIN` env. */\nconst DEFAULT_E2B_DOMAIN = 'e2b.app';\n\n/**\n * Per-box session timeout the SDK enforces. Past it, E2B auto-terminates the\n * sandbox; we explicitly extend via `sb.setTimeout` is not needed for Task 1's\n * smoke (boxes survive minutes, not hours). 45 min default mirrors vercel.\n */\nconst DEFAULT_TIMEOUT_MS = 45 * 60_000;\n\nconst E2B_WEB_PORT = 8080;\n\n/** Single-quote a string for safe embedding inside a `bash -lc '<…>'`. */\nfunction shq(s: string): string {\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/**\n * Convert a Node `Buffer` to a plain `ArrayBuffer` because E2B's `files.write`\n * `data:` field is `string | ArrayBuffer | Blob | ReadableStream` — Buffer is a\n * `Uint8Array` subclass and doesn't satisfy that union at the type level (even\n * though it works at runtime). Copy rather than slice the underlying buffer:\n * Buffers may share an underlying ArrayBuffer with a pooled allocator, so\n * `data.buffer` of a small Buffer can be a megabyte-long shared region.\n */\nfunction bufferToArrayBuffer(b: Buffer): ArrayBuffer {\n const ab = new ArrayBuffer(b.byteLength);\n new Uint8Array(ab).set(b);\n return ab;\n}\n\n/**\n * Map the SDK's nullable string state onto our 4-value `CloudState`.\n * E2B reports 'running' | 'paused' (per SDK types). Anything else (or absent)\n * → 'missing' so callers ping-pong the lifecycle into a clean state.\n */\nfunction mapState(s: SandboxState | undefined): CloudState {\n switch (s) {\n case 'running':\n return 'running';\n case 'paused':\n return 'paused';\n default:\n return 'missing';\n }\n}\n\n/** True when the error means \"sandbox doesn't exist\" (404). */\nfunction isNotFound(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const name = err instanceof Error ? err.name : '';\n if (name === 'SandboxNotFoundError' || name === 'NotFoundError') return true;\n const status = (err as { statusCode?: unknown; status?: unknown }).statusCode ?? (err as { status?: unknown }).status;\n return status === 404;\n}\n\n/**\n * Sanitize a box name for use as the `metadata.name` value. E2B accepts\n * arbitrary strings (probed) but we strip control chars defensively so a name\n * with embedded newlines can't break log parsing or response shapes.\n */\nfunction safeMetadataName(name: string): string {\n return name.replace(/[\\u0000-\\u001f]/g, '').slice(0, 200);\n}\n\nexport const e2bBackend: CloudBackend = {\n name: 'e2b',\n\n // The cloud scaffold's WebProxy binds whatever port we expose here, and\n // `agentbox url --kind=web` resolves via `getHost(port)`. 8080 matches the\n // non-privileged convention vercel uses — `getHost` accepts any port, but\n // staying on 8080 keeps the in-box ctl flag (AGENTBOX_WEB_PROXY_PORT)\n // identical across cloud providers.\n webProxyPort: E2B_WEB_PORT,\n\n async provision(req: CloudProvisionRequest): Promise<CloudHandle> {\n const apiKey = resolveApiKey();\n const log = req.onLog ?? (() => {});\n // Resolve the template to boot from: an explicit cloud-checkpoint snapshot\n // (req.snapshot) wins, else the prepared base template id. We don't fall\n // back to E2B's stock `base` template — every box must have the agentbox\n // runtime baked in. The gate throws an actionable \"run `agentbox prepare`\"\n // error when no template is recorded yet (mirrors the hetzner/vercel\n // pattern: `prepare` itself sidesteps the gate by calling `prepareE2b`\n // directly, never `provision`).\n if (req.snapshot === undefined) {\n ensureE2bBaseTemplate();\n }\n const template = req.snapshot ?? readPreparedState().base?.templateId;\n if (!template) {\n throw new Error(\n 'e2b provision: no template available — `agentbox prepare --provider e2b` must run first',\n );\n }\n\n // No-retry: Sandbox.create is billable and non-idempotent — a timeout\n // after the request reached the origin could leave a duplicate sandbox we\n // can't reference for cleanup.\n const sb = await withE2bRetry(\n { method: 'provision', retryOnAmbiguous: false, attemptTimeoutMs: 300_000, backoffMs: [] },\n async () =>\n Sandbox.create({\n apiKey,\n template,\n // Friendly name (so prune can see it) + the 'agentbox' marker so\n // `list()` can filter out sandboxes provisioned by other tooling.\n metadata: { agentbox: 'true', 'agentbox.name': safeMetadataName(req.name), name: safeMetadataName(req.name) },\n envs: req.env,\n timeoutMs: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n }),\n );\n log(`e2b: created sandbox ${sb.sandboxId} (template ${template})`);\n return { sandboxId: sb.sandboxId };\n },\n\n async get(sandboxId: string): Promise<CloudHandle | null> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'get', retryOnAmbiguous: true }, async () => {\n try {\n // Static, NON-resuming — won't wake a paused sandbox just to confirm\n // it exists (per orchestrator review #1: connect would auto-resume).\n await Sandbox.getInfo(sandboxId, { apiKey });\n return { sandboxId };\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n });\n },\n\n async list(): Promise<CloudSandboxSummary[]> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'list', retryOnAmbiguous: true }, async () => {\n const summaries: CloudSandboxSummary[] = [];\n // Default query returns both running and paused sandboxes. We filter\n // client-side to the ones we created (metadata.agentbox === 'true').\n for (const state of ['running', 'paused'] as const) {\n const paginator = Sandbox.list({ apiKey, query: { state: [state] } });\n while (paginator.hasNext) {\n const page = await paginator.nextItems();\n for (const info of page) {\n if (info.metadata?.['agentbox'] !== 'true') continue;\n const friendly =\n info.metadata?.['agentbox.name'] ?? info.metadata?.['name'];\n const summary: CloudSandboxSummary = { sandboxId: info.sandboxId, state };\n if (friendly) summary.name = friendly;\n const startedAt = info.startedAt;\n if (startedAt instanceof Date) summary.createdAt = startedAt.toISOString();\n summaries.push(summary);\n }\n }\n }\n return summaries;\n });\n },\n\n // E2B has no separate stop primitive — sandboxes are either running or\n // paused. start is therefore a connect-and-resume (auto-resume inside\n // Sandbox.connect handles a paused box transparently).\n async start(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'start', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n await Sandbox.connect(h.sandboxId, { apiKey });\n },\n );\n },\n\n // stop ≡ pause on E2B (the pause IS the cold-storage state).\n async stop(h: CloudHandle): Promise<void> {\n await this.pause(h);\n },\n\n async pause(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'pause', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n await Sandbox.pause(h.sandboxId, { apiKey });\n },\n );\n },\n\n async resume(h: CloudHandle): Promise<void> {\n await this.start(h);\n },\n\n async destroy(h: CloudHandle): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'destroy', retryOnAmbiguous: true, attemptTimeoutMs: 120_000 },\n async () => {\n try {\n await Sandbox.kill(h.sandboxId, { apiKey });\n } catch (err) {\n if (isNotFound(err)) return; // idempotent\n throw err;\n }\n },\n );\n },\n\n async state(h: CloudHandle): Promise<CloudState> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'state', retryOnAmbiguous: true }, async () => {\n try {\n const info: SandboxInfo = await Sandbox.getInfo(h.sandboxId, { apiKey });\n return mapState(info.state);\n } catch (err) {\n if (isNotFound(err)) return 'missing';\n throw err;\n }\n });\n },\n\n async exec(h: CloudHandle, cmd: string, opts?: CloudExecOptions): Promise<CloudExecResult> {\n const apiKey = resolveApiKey();\n // Default per-attempt cap is 5 min — covers the cloud scaffold's\n // workspace-seed/carry extracts (tar of thousands of files, chown -R).\n // Callers can shorten with opts.attemptTimeoutMs for snappier probes.\n const timeoutMs = opts?.attemptTimeoutMs ?? 300_000;\n return withE2bRetry(\n {\n method: 'exec',\n retryOnAmbiguous: opts?.noRetry ? false : true,\n attemptTimeoutMs: timeoutMs,\n backoffMs: opts?.noRetry ? [] : undefined,\n },\n async () => {\n // Connect for the live handle — auto-resumes a paused box, which is\n // the correct semantics for exec (caller wants the command to run).\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n // E2B's `commands.run` accepts only 'root' | 'user' | 'vscode'…; any\n // unix username we create in the fixup is valid. Pass through.\n const user = (opts?.user ?? BOX_USER) as 'root' | 'user';\n try {\n const r = await sb.commands.run(cmd, {\n ...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts?.env !== undefined ? { envs: opts.env } : {}),\n user,\n timeoutMs,\n });\n return { exitCode: r.exitCode, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };\n } catch (err) {\n // commands.run throws on non-zero exit; the CommandResult fields\n // (exitCode/stdout/stderr) hang off the error. Map back into our\n // CloudExecResult so callers see exit=1, not a thrown exception\n // (vercel/daytona/hetzner exec contract returns the result).\n if (err instanceof Error && err.name === 'CommandExitError') {\n const ce = err as unknown as {\n exitCode: number;\n stdout: string;\n stderr: string;\n };\n return { exitCode: ce.exitCode, stdout: ce.stdout ?? '', stderr: ce.stderr ?? '' };\n }\n throw err;\n }\n },\n );\n },\n\n async uploadFile(h: CloudHandle, localPath: string, remotePath: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'uploadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const data = await readFile(localPath);\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n await sb.files.write([{ path: remotePath, data: bufferToArrayBuffer(data) }]);\n // files.write writes as the default user; chown to vscode so reads\n // from the scaffold's `sudo -u vscode …` exec calls succeed. Best-\n // effort — a chown failure on a world-readable file is harmless.\n try {\n await sb.commands.run(`sudo -n chown ${BOX_OWNER} ${shq(remotePath)}`, {\n user: 'root',\n timeoutMs: 10_000,\n });\n } catch {\n // ignore — file is at least present and readable\n }\n },\n );\n },\n\n async downloadFile(h: CloudHandle, remotePath: string, localPath: string): Promise<void> {\n const apiKey = resolveApiKey();\n await withE2bRetry(\n { method: 'downloadFile', retryOnAmbiguous: true, attemptTimeoutMs: 300_000 },\n async () => {\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n const bytes = await sb.files.read(remotePath, { format: 'bytes' });\n const { writeFile } = await import('node:fs/promises');\n await writeFile(localPath, Buffer.from(bytes));\n },\n );\n },\n\n async listFiles(h: CloudHandle, remoteDir: string): Promise<CloudFileEntry[]> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'listFiles', retryOnAmbiguous: true }, async () => {\n const sb = await Sandbox.connect(h.sandboxId, { apiKey });\n const entries = await sb.files.list(remoteDir);\n return entries.map((e) => ({ name: e.name, isDir: e.type === 'dir' }));\n });\n },\n\n async previewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n // E2B's `sandbox.getHost(port)` is just string interpolation of\n // `${port}-${sandboxId}.${sandboxDomain}` — calling it via Sandbox.connect\n // would auto-resume a paused box (the SDK's documented behavior). The\n // domain defaults to `e2b.app` with `E2B_DOMAIN` as the override (matches\n // the SDK's `ConnectionConfig` default), so we construct the URL locally\n // and never wake the box for a UI/dashboard URL fetch.\n const domain = process.env.E2B_DOMAIN ?? DEFAULT_E2B_DOMAIN;\n return { url: `https://${String(port)}-${h.sandboxId}.${domain}`, token: undefined };\n },\n\n // Fewer params than the interface's (h, port, expiresInSeconds) is fine —\n // E2B preview URLs are already public + browser-usable; no per-URL TTL.\n async signedPreviewUrl(h: CloudHandle, port: number): Promise<CloudPreviewUrl> {\n return this.previewUrl(h, port);\n },\n\n /**\n * Probe whether a snapshot (i.e. a template id) is still bootable. E2B's\n * snapshot ids look like `template-id:tag` or `team-slug/name:tag` — both\n * accepted by `Template.exists(name)`. Returns false on any lookup failure\n * (treated by the cloud-provider as \"gone\" so it falls back to a from-base\n * boot rather than 410ing the user).\n */\n async snapshotExists(snapshotName: string): Promise<boolean> {\n const apiKey = resolveApiKey();\n return withE2bRetry({ method: 'snapshotExists', retryOnAmbiguous: true }, async () => {\n try {\n return await Template.exists(snapshotName, { apiKey });\n } catch {\n return false;\n }\n });\n },\n};\n","/**\n * Bounded retry wrapper for E2B SDK calls — mirrors `withVercelRetry` /\n * `withHetznerRetry` in shape and intent. The E2B API rate-limits (429) and\n * can return transient 5xx during incidents; without bounded retries those\n * propagate as wedges in the calling lifecycle code.\n *\n * Non-idempotent ops (`provision`/`Sandbox.create`) pass `retryOnAmbiguous:\n * false` so a timeout after the request reached the origin doesn't create a\n * duplicate billable sandbox.\n */\n\nexport interface WithRetryOptions {\n method: string;\n /** Per-attempt timeout (ms). Default 30_000. */\n attemptTimeoutMs?: number;\n /** Backoff before attempts 2, 3, … (ms). Default [1000, 2000, 4000]. */\n backoffMs?: readonly number[];\n /**\n * Retry on errors where we can't be sure the server applied the request\n * (connection failures, per-attempt timeouts, 5xx). Set false for\n * non-idempotent operations where a retry could create a duplicate resource.\n */\n retryOnAmbiguous: boolean;\n /** Override the default stderr retry sink (used by tests). */\n onRetry?: (line: string) => void;\n}\n\nconst DEFAULT_BACKOFF: readonly number[] = [1000, 2000, 4000];\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 30_000;\n\nclass AttemptTimeoutError extends Error {\n constructor(method: string, ms: number) {\n super(`e2b ${method}: per-attempt timeout after ${String(ms)}ms`);\n this.name = 'AttemptTimeoutError';\n }\n}\n\nexport function isAttemptTimeout(err: unknown): err is AttemptTimeoutError {\n return err instanceof AttemptTimeoutError;\n}\n\n/** HTTP status code dug out of whatever error shape the SDK throws. */\nfunction statusCodeOf(err: unknown): number | undefined {\n if (!err || typeof err !== 'object') return undefined;\n for (const key of ['statusCode', 'status', 'code'] as const) {\n const v = (err as Record<string, unknown>)[key];\n if (typeof v === 'number') return v;\n }\n const resp = (err as { response?: { status?: unknown } }).response;\n if (resp && typeof resp.status === 'number') return resp.status;\n return undefined;\n}\n\nexport function isRetriable(err: unknown, allowAmbiguous: boolean): boolean {\n if (err instanceof AttemptTimeoutError) return allowAmbiguous;\n\n // The SDK exposes a dedicated `SandboxNotFoundError` and `RateLimitError`.\n // Match by error name to avoid an import cycle (sdk.ts → retry → sdk).\n const name = err instanceof Error ? err.name : undefined;\n if (name === 'RateLimitError') return true;\n if (name === 'SandboxNotFoundError') return false;\n\n const status = statusCodeOf(err);\n if (status !== undefined) {\n if (status === 429) return true;\n if (status >= 500 && status <= 599) return allowAmbiguous;\n return false;\n }\n\n // Raw fetch / undici errors. Node wraps low-level errors in `{ cause }`.\n if (err && typeof err === 'object') {\n const candidates: unknown[] = [err, (err as { cause?: unknown }).cause];\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue;\n const code = (c as { code?: unknown }).code;\n if (\n code === 'ECONNRESET' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNABORTED' ||\n code === 'EAI_AGAIN' ||\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'UND_ERR_SOCKET' ||\n code === 'UND_ERR_CONNECT_TIMEOUT'\n ) {\n return allowAmbiguous;\n }\n }\n }\n return false;\n}\n\nexport async function withE2bRetry<T>(opts: WithRetryOptions, fn: () => Promise<T>): Promise<T> {\n const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;\n const maxAttempts = backoff.length + 1;\n const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const log = opts.onRetry ?? defaultRetryLog;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await raceTimeout(fn(), timeoutMs, opts.method);\n } catch (err) {\n const last = attempt === maxAttempts;\n if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;\n const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4000;\n log(\n `e2b ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`,\n );\n await sleep(delay);\n }\n }\n throw new Error(`withE2bRetry: exhausted attempts for ${opts.method}`);\n}\n\nfunction defaultRetryLog(line: string): void {\n process.stderr.write(`\\n[e2b-retry] ${line}\\n`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function raceTimeout<T>(p: Promise<T>, ms: number, method: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n p,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\nfunction errorSummary(err: unknown): string {\n if (err instanceof Error) {\n const status = statusCodeOf(err);\n return status !== undefined\n ? `${err.name}(${String(status)}): ${truncate(err.message)}`\n : `${err.name}: ${truncate(err.message)}`;\n }\n return truncate(String(err));\n}\n\nfunction truncate(s: string, max = 160): string {\n return s.length > max ? `${s.slice(0, max)}…` : s;\n}\n","/**\n * `agentbox prepare --provider e2b` — bake the E2B base template.\n *\n * Unlike Vercel/Hetzner, E2B can build templates from a build DSL (the SDK's\n * `Template` + `Template.build`). We mirror Vercel's `prepare` shape but drive\n * the build through the SDK builder API instead of booting a sandbox + running\n * `provision.sh`:\n *\n * 1. Resolve runtime assets + fingerprint the build context. Skip-fast when\n * an up-to-date template id is already recorded.\n * 2. Stage every resolved asset under a temp `fileContextPath` directory\n * with predictable relative names (E2B's `template.copy(src, dest)`\n * requires sources to be RELATIVE paths inside the context dir).\n * 3. `Template({ fileContextPath })` → `.fromBaseImage()` (E2B's default\n * Debian 12 + node 20 + git + sudo). `.copy(rel, dest)` for each asset,\n * `.runCmd('bash /tmp/agentbox-build-template.sh', { user: 'root' })`,\n * `.setReadyCmd('test -x /usr/local/bin/agentbox-ctl')`.\n * 4. `Template.build(t, 'agentbox-base:<tag>', { cpuCount, memoryMB,\n * onBuildLogs })` streams logs into the spinner; returns the BuildInfo\n * with the template id.\n * 5. Persist `{ schema:1, base: { templateId, contextSha256, cliVersion,\n * cliCommit, createdAt } }` to ~/.agentbox/e2b-prepared.json.\n *\n * Templates on E2B are reusable named resources addressed by id+tag. Re-running\n * with the same name reuses the existing template id (E2B's documented\n * behavior). Unlike Vercel snapshots there's no per-box eviction concern; one\n * template is reused for every create.\n *\n * vCPU / RAM are template-level on E2B — set them here so per-box `create`\n * doesn't try to override them (which E2B rejects).\n */\n\nimport { copyFile, mkdir, mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport type { Provider } from '@agentbox/core';\nimport { computeContextSha256, readCliStamp } from '@agentbox/sandbox-core';\nimport { ensureE2bCredentials } from './credentials.js';\nimport { resolveApiKey, Template } from './sdk.js';\nimport {\n preparedStatePath,\n readPreparedState,\n writePreparedState,\n} from './prepared-state.js';\nimport {\n findStagedCliRuntimeRoot,\n resolveRuntimeAssets,\n type ResolvedAsset,\n} from './runtime-assets.js';\n\nexport interface PrepareE2bOptions {\n name?: string;\n hostWorkspace?: string;\n /** Force re-bake even when an up-to-date template id is recorded. */\n force?: boolean;\n /** vCPUs for the baked template (default 2). E2B applies this per-sandbox at boot. */\n cpuCount?: number;\n /** Memory in MiB for the baked template (default 4096). */\n memoryMB?: number;\n /** CLI runtime tree (set by the CLI to its dist neighbor). */\n cliRuntimeRoot?: string;\n /** Repo root for the dev fallback (defaults to a cwd-walk). */\n repoRoot?: string;\n onLog?: (line: string) => void;\n}\n\nexport interface PrepareE2bResult {\n snapshotName?: string;\n}\n\n/** Template name agentbox bakes under. E2B treats `name:tag` as a single addressable build. */\nconst TEMPLATE_NAME = 'agentbox-base:latest';\nconst DEFAULT_TAG = 'latest';\n\nconst DEFAULT_CPU = 2;\nconst DEFAULT_MEMORY_MB = 4096;\n\nexport async function prepareE2b(\n opts: PrepareE2bOptions = {},\n): Promise<PrepareE2bResult> {\n await ensureE2bCredentials();\n const apiKey = resolveApiKey();\n const log = opts.onLog ?? (() => {});\n const progress = (s: string) => log(`prepare-e2b: ${s}`);\n\n const assets = resolveRuntimeAssets({\n cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),\n repoRoot: opts.repoRoot,\n });\n const contextSha = await computeContextSha256(\n assets.map((a) => ({ rel: a.name, abs: a.localPath })),\n );\n\n // Skip-fast: existing template + matching fingerprint.\n //\n // Probe the persisted templateId itself, not TEMPLATE_NAME. If someone\n // (or another bake) rebuilt the alias under a different id, the name still\n // resolves but the stored id is stale and `Sandbox.create({ template: <stale id> })`\n // 404s. `Template.exists` accepts both `name:tag` and `template-id:tag`\n // forms, so we pass the exact id we'd later hand to `provision`.\n const existing = readPreparedState();\n if (!opts.force && existing.base) {\n if (existing.base.contextSha256 === contextSha) {\n const stillThere = await templateExists(existing.base.templateId, apiKey);\n if (stillThere) {\n progress(\n `template ${existing.base.templateId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`,\n );\n return { snapshotName: existing.base.templateId };\n }\n progress(`recorded template ${existing.base.templateId} is gone on E2B; rebuilding`);\n } else {\n progress(\n `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? '<none>'}, now ${contextSha.slice(0, 12)}); rebuilding`,\n );\n }\n }\n\n // E2B's `template.copy(src, dest)` requires `src` to be a RELATIVE path\n // inside the Template's `fileContextPath`. Stage every resolved asset into\n // a temp dir under its logical name (asset.name) so the copy chain reads\n // from a single context root.\n const contextDir = await mkdtemp(join(tmpdir(), 'agentbox-e2b-build-'));\n try {\n progress(`staging build context at ${contextDir}`);\n await stageAssetsInto(contextDir, assets);\n\n // Build the Template via the SDK builder. fromBaseImage() starts from E2B's\n // own `e2bdev/base` (Debian 12 + node 20 + git + sudo), which halves the\n // install time vs starting from a vanilla Debian image.\n progress('assembling template build (fromBaseImage + asset copy + runCmd)');\n const template = Template({ fileContextPath: contextDir }).fromBaseImage();\n for (const a of assets) {\n progress(` copy ${a.name} -> ${a.remotePath}`);\n template.copy(a.name, a.remotePath, {\n forceUpload: true,\n mode: a.remoteMode,\n user: 'root',\n });\n }\n template.runCmd('bash /tmp/agentbox-build-template.sh 2>&1', { user: 'root' });\n // setReadyCmd flips the builder into TemplateFinal — required for build().\n // The check passes once the script's last `install` step lands the ctl bundle.\n const finalTemplate = template.setReadyCmd(\n 'test -x /usr/local/bin/agentbox-ctl',\n );\n\n const cpuCount = opts.cpuCount ?? DEFAULT_CPU;\n const memoryMB = opts.memoryMB ?? DEFAULT_MEMORY_MB;\n progress(\n `running Template.build('${TEMPLATE_NAME}', { cpuCount: ${String(cpuCount)}, memoryMB: ${String(memoryMB)} })`,\n );\n const info = await Template.build(finalTemplate, TEMPLATE_NAME, {\n apiKey,\n cpuCount,\n memoryMB,\n onBuildLogs: (entry: LogEntryLike) => {\n // LogEntry exposes timestamp / level / message; stream the human form.\n log(`[build] ${formatBuildLog(entry)}`);\n },\n });\n progress(`template built: id=${info.templateId} build=${info.buildId} name=${info.name}`);\n\n // Persist. `Sandbox.create({ template })` auto-appends `:default` when no\n // tag is given (and 404s if that tag wasn't built), so we MUST store the\n // tagged form. `info.templateId` is just the raw id with no tag; use the\n // first tag we built with (`latest`) or fall back to `info.tags[0]`.\n const tag = info.tags?.[0] ?? DEFAULT_TAG;\n const cliStamp = readCliStamp();\n const taggedId = `${info.templateId}:${tag}`;\n writePreparedState({\n schema: 1,\n base: {\n templateId: taggedId,\n // info.name is the full `name:tag` pair Template.build() was called\n // with (e.g. `agentbox-base:latest`). Earlier code re-appended `:${tag}`\n // and produced `agentbox-base:latest:latest` in the status display.\n templateName: info.name,\n contextSha256: contextSha,\n cliVersion: cliStamp.cliVersion,\n cliCommit: cliStamp.cliCommit,\n createdAt: new Date().toISOString(),\n },\n });\n progress(`wrote ${preparedStatePath()}`);\n\n progress(`prepare complete — base template ${taggedId}`);\n return { snapshotName: taggedId };\n } finally {\n await rm(contextDir, { recursive: true, force: true }).catch(() => {\n // best-effort: temp dir cleanup failures are noise, not errors.\n });\n }\n}\n\n/**\n * Copy every asset into `contextDir` under its logical `name`. Preserves the\n * source mode on the copy (E2B's `template.copy` also accepts a `mode`\n * override, but the on-disk mode keeps the local stage representative).\n */\nasync function stageAssetsInto(\n contextDir: string,\n assets: ResolvedAsset[],\n): Promise<void> {\n for (const a of assets) {\n const dest = resolve(contextDir, a.name);\n await mkdir(dirname(dest), { recursive: true });\n await copyFile(a.localPath, dest);\n }\n}\n\n/**\n * Check if a named template is bootable on E2B. Returns true on a 'ready'\n * build, false on anything else (deleted, errored, never built). Used by the\n * skip-fast path to detect a template that was deleted out-of-band.\n */\nasync function templateExists(name: string, apiKey: string): Promise<boolean> {\n try {\n return await Template.exists(name, { apiKey });\n } catch {\n return false;\n }\n}\n\n/**\n * E2B's `LogEntry` shape (timestamp, level, message). We treat the SDK's\n * type loosely here so the line-stream doesn't bind us to internal class\n * shapes — only the `.toString()` plus `.message` are documented.\n */\ninterface LogEntryLike {\n message?: string;\n level?: string;\n timestamp?: Date;\n toString(): string;\n}\n\nfunction formatBuildLog(entry: LogEntryLike): string {\n // The SDK's LogEntry.toString() emits a `[level] timestamp message` form.\n // For the spinner we only want the message — and clip overly long lines.\n const raw = typeof entry.message === 'string' ? entry.message : entry.toString();\n const cleaned = raw.replace(/\\r?\\n+$/, '');\n return cleaned.length > 200 ? cleaned.slice(0, 200) + '…' : cleaned;\n}\n\n/** Provider-level binding used by the CLI's `prepare` command. */\nexport const prepareE2bProvider: NonNullable<Provider['prepare']> = (req) =>\n prepareE2b({\n name: req.name,\n hostWorkspace: req.hostWorkspace ?? process.cwd(),\n force: req.force,\n onLog: req.onLog,\n });\n","/**\n * `buildE2bAttach` — the E2B provider's override of `Provider.buildAttach`.\n *\n * E2B has no SSH and no public PTY CLI like Vercel's `sbx exec -i`. We ship\n * our own attach helper (`attach-helper.cjs`) that connects to the sandbox via\n * the SDK and bridges the host PTY to an in-box `sandbox.pty.create()`.\n *\n * Argv shape:\n * node <attach-helper.cjs> --sandbox-id <id> --user vscode\n *\n * The inner bash command (`renderInnerCommand` output: tmux ensure + attach)\n * is passed through the environment as `AGENTBOX_E2B_INNER_CMD` so quoting\n * stays sane and it doesn't leak through `ps`. `E2B_API_KEY` is passed through\n * the env for the same reason. `node-pty` (the host PTY wrapper) is what\n * spawns this argv, so stdin/stdout/SIGWINCH all reach the helper unchanged.\n */\n\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type AttachKind,\n type AttachSpec,\n type BoxRecord,\n type BuildAttachOptions,\n} from '@agentbox/core';\nimport { renderInnerCommand } from '@agentbox/sandbox-cloud';\nimport { resolveApiKey } from './sdk.js';\n\nconst SELF = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the absolute path to `attach-helper.cjs`. In the published CLI it\n * lives at `runtime/e2b/attach-helper.cjs` (next to the staged provider\n * runtime tree); in dev it lives next to the package's `dist/`.\n */\nexport function resolveAttachHelperPath(): string {\n const candidates = [\n // dev: dist/index.js sibling\n resolve(SELF, 'attach-helper.cjs'),\n // dev: src compiled to dist/, while index.ts is in src/\n resolve(SELF, '..', 'dist', 'attach-helper.cjs'),\n // staged CLI: apps/cli/runtime/e2b/attach-helper.cjs\n resolve(SELF, '..', 'runtime', 'e2b', 'attach-helper.cjs'),\n resolve(SELF, '..', '..', 'runtime', 'e2b', 'attach-helper.cjs'),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n // Last-resort: still return the first candidate so the error message points\n // somewhere informative.\n return candidates[0]!;\n}\n\nexport async function buildE2bAttach(\n box: BoxRecord,\n kind: AttachKind,\n opts?: BuildAttachOptions,\n): Promise<AttachSpec> {\n const sandboxId = box.cloud?.sandboxId;\n if (!sandboxId) {\n throw new Error(`e2b box ${box.name} has no sandboxId — record is malformed`);\n }\n\n const helper = resolveAttachHelperPath();\n if (!existsSync(helper)) {\n throw new Error(\n `e2b attach helper not found at ${helper} — rebuild the CLI (\\`pnpm -w build\\`) ` +\n 'so packages/sandbox-e2b/dist/attach-helper.cjs is generated.',\n );\n }\n\n const apiKey = resolveApiKey();\n const inner = renderInnerCommand(kind, opts);\n\n const argv = [\n process.execPath,\n helper,\n '--sandbox-id',\n sandboxId,\n '--user',\n 'vscode',\n ];\n\n return {\n argv,\n env: {\n E2B_API_KEY: apiKey,\n AGENTBOX_E2B_INNER_CMD: inner,\n },\n };\n}\n"],"mappings":"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","names":["dirname","resolve","snapshotName","dirname","resolve","readCliStamp"]}
|
|
@@ -26,21 +26,22 @@ import {
|
|
|
26
26
|
updatePreparedState,
|
|
27
27
|
withHetznerRetry,
|
|
28
28
|
writePreparedState
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-3JXSW6PN.js";
|
|
30
30
|
import {
|
|
31
31
|
createCloudProvider
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-KTDJ5JTE.js";
|
|
33
33
|
import {
|
|
34
34
|
stageClaudeStaticForUpload,
|
|
35
35
|
stageCodexStaticForUpload,
|
|
36
36
|
stageOpencodeStaticForUpload
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-HFQZJO73.js";
|
|
38
38
|
import {
|
|
39
39
|
UserFacingError,
|
|
40
40
|
computeContextSha256,
|
|
41
41
|
readCliStamp
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-6QFPYU4Z.js";
|
|
43
43
|
import "./chunk-G3H2L3O2.js";
|
|
44
|
+
import "./chunk-WJFZJZIM.js";
|
|
44
45
|
|
|
45
46
|
// ../../packages/sandbox-hetzner/dist/index.js
|
|
46
47
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -1228,4 +1229,4 @@ export {
|
|
|
1228
1229
|
withHetznerRetry,
|
|
1229
1230
|
writePreparedState
|
|
1230
1231
|
};
|
|
1231
|
-
//# sourceMappingURL=dist-
|
|
1232
|
+
//# sourceMappingURL=dist-CNABE32V.js.map
|