@smithers-orchestrator/sandbox 0.20.3 → 0.21.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.
- package/package.json +6 -9
- package/src/ExecuteSandboxOptions.ts +17 -13
- package/src/SandboxBundleManifest.ts +7 -4
- package/src/SandboxHandle.ts +26 -0
- package/src/SandboxProvider.ts +59 -0
- package/src/SandboxTransportConfig.ts +8 -0
- package/src/bundle.js +25 -9
- package/src/effect/http-runner.js +23 -5
- package/src/effect/process-runner.js +428 -0
- package/src/effect/sandbox-entity.js +34 -0
- package/src/effect/socket-runner.js +33 -2
- package/src/execute.js +421 -26
- package/src/index.d.ts +96 -1
- package/src/sandboxPath.js +1 -5
- package/src/transport.js +11 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithers-orchestrator/sandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Sandbox bundle, execution, and transport integration for Smithers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -23,14 +23,11 @@
|
|
|
23
23
|
"@effect/cluster": "^0.58.0",
|
|
24
24
|
"@effect/rpc": "^0.75.0",
|
|
25
25
|
"effect": "^3.21.1",
|
|
26
|
-
"@smithers-orchestrator/db": "0.
|
|
27
|
-
"@smithers-orchestrator/driver": "0.
|
|
28
|
-
"@smithers-orchestrator/
|
|
29
|
-
"@smithers-orchestrator/
|
|
30
|
-
"@smithers-orchestrator/
|
|
31
|
-
"@smithers-orchestrator/graph": "0.20.3",
|
|
32
|
-
"@smithers-orchestrator/observability": "0.20.3",
|
|
33
|
-
"@smithers-orchestrator/scheduler": "0.20.3"
|
|
26
|
+
"@smithers-orchestrator/db": "0.21.0",
|
|
27
|
+
"@smithers-orchestrator/driver": "0.21.0",
|
|
28
|
+
"@smithers-orchestrator/errors": "0.21.0",
|
|
29
|
+
"@smithers-orchestrator/observability": "0.21.0",
|
|
30
|
+
"@smithers-orchestrator/scheduler": "0.21.0"
|
|
34
31
|
},
|
|
35
32
|
"devDependencies": {
|
|
36
33
|
"@types/bun": "latest",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SandboxRuntime } from "./SandboxRuntime.ts";
|
|
2
|
+
import type { SandboxDiffBundleLike, SandboxProvider } from "./SandboxProvider.ts";
|
|
2
3
|
|
|
3
4
|
export type SandboxWorkflow = {
|
|
4
5
|
db?: unknown;
|
|
@@ -31,17 +32,20 @@ export type ExecuteSandboxChildWorkflow = (
|
|
|
31
32
|
) => Promise<{ runId: string; status: string; output: unknown }>;
|
|
32
33
|
|
|
33
34
|
export type ExecuteSandboxOptions = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
parentWorkflow?: SandboxWorkflow;
|
|
36
|
+
sandboxId: string;
|
|
37
|
+
provider?: SandboxProvider | string;
|
|
38
|
+
runtime?: SandboxRuntime;
|
|
39
|
+
workflow: SandboxChildWorkflowDefinition;
|
|
40
|
+
executeChildWorkflow: ExecuteSandboxChildWorkflow;
|
|
41
|
+
applyDiffBundle?: (bundle: SandboxDiffBundleLike, targetDir: string) => Promise<void>;
|
|
42
|
+
input?: unknown;
|
|
43
|
+
rootDir: string;
|
|
44
|
+
allowNetwork: boolean;
|
|
45
|
+
maxOutputBytes: number;
|
|
46
|
+
toolTimeoutMs: number;
|
|
47
|
+
reviewDiffs?: boolean;
|
|
48
|
+
autoAcceptDiffs?: boolean;
|
|
49
|
+
allowNested?: boolean;
|
|
50
|
+
config?: Record<string, unknown>;
|
|
47
51
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { SandboxDiffBundleLike } from "./SandboxProvider.ts";
|
|
2
|
+
|
|
1
3
|
export type SandboxBundleManifest = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
outputs: unknown;
|
|
5
|
+
status: "finished" | "failed" | "cancelled";
|
|
6
|
+
runId?: string;
|
|
7
|
+
patches?: string[];
|
|
8
|
+
diffBundle?: SandboxDiffBundleLike;
|
|
6
9
|
};
|
package/src/SandboxHandle.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import type { SandboxRuntime } from "./SandboxRuntime.ts";
|
|
2
2
|
|
|
3
|
+
export type SandboxPortMapping = {
|
|
4
|
+
host: number;
|
|
5
|
+
container: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SandboxVolumeMount = {
|
|
9
|
+
host: string;
|
|
10
|
+
container: string;
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type SandboxWorkspaceSpec = {
|
|
15
|
+
name: string;
|
|
16
|
+
snapshotId?: string;
|
|
17
|
+
idleTimeoutSecs?: number;
|
|
18
|
+
persistence?: "ephemeral" | "sticky";
|
|
19
|
+
};
|
|
20
|
+
|
|
3
21
|
export type SandboxHandle = {
|
|
4
22
|
runtime: SandboxRuntime;
|
|
5
23
|
runId: string;
|
|
@@ -7,6 +25,14 @@ export type SandboxHandle = {
|
|
|
7
25
|
sandboxRoot: string;
|
|
8
26
|
requestPath: string;
|
|
9
27
|
resultPath: string;
|
|
28
|
+
image?: string;
|
|
29
|
+
allowNetwork?: boolean;
|
|
30
|
+
env?: Record<string, string>;
|
|
31
|
+
ports?: SandboxPortMapping[];
|
|
32
|
+
volumes?: SandboxVolumeMount[];
|
|
33
|
+
memoryLimit?: string;
|
|
34
|
+
cpuLimit?: string;
|
|
35
|
+
workspace?: SandboxWorkspaceSpec;
|
|
10
36
|
containerId?: string;
|
|
11
37
|
workspaceId?: string;
|
|
12
38
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { SandboxChildWorkflowDefinition, SandboxWorkflow, ExecuteSandboxChildWorkflow } from "./ExecuteSandboxOptions.ts";
|
|
2
|
+
|
|
3
|
+
export type SandboxBundleStatus = "finished" | "failed" | "cancelled";
|
|
4
|
+
|
|
5
|
+
export type SandboxDiffBundleLike = {
|
|
6
|
+
seq: number;
|
|
7
|
+
baseRef: string;
|
|
8
|
+
patches: Array<{
|
|
9
|
+
path: string;
|
|
10
|
+
operation: "add" | "modify" | "delete";
|
|
11
|
+
diff: string;
|
|
12
|
+
binaryContent?: string;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type SandboxProviderRequest = {
|
|
17
|
+
runId: string;
|
|
18
|
+
sandboxId: string;
|
|
19
|
+
input?: unknown;
|
|
20
|
+
rootDir: string;
|
|
21
|
+
requestBundlePath: string;
|
|
22
|
+
resultBundlePath: string;
|
|
23
|
+
workflow: SandboxChildWorkflowDefinition;
|
|
24
|
+
parentWorkflow?: SandboxWorkflow;
|
|
25
|
+
executeChildWorkflow: ExecuteSandboxChildWorkflow;
|
|
26
|
+
allowNetwork: boolean;
|
|
27
|
+
maxOutputBytes: number;
|
|
28
|
+
toolTimeoutMs: number;
|
|
29
|
+
config: Record<string, unknown>;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
heartbeat: (data?: unknown) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type SandboxProviderResult =
|
|
35
|
+
| {
|
|
36
|
+
bundlePath: string;
|
|
37
|
+
remoteRunId?: string;
|
|
38
|
+
workspaceId?: string;
|
|
39
|
+
containerId?: string;
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
status: SandboxBundleStatus;
|
|
43
|
+
output?: unknown;
|
|
44
|
+
outputs?: unknown;
|
|
45
|
+
runId?: string;
|
|
46
|
+
remoteRunId?: string;
|
|
47
|
+
workspaceId?: string;
|
|
48
|
+
containerId?: string;
|
|
49
|
+
diffBundle?: SandboxDiffBundleLike;
|
|
50
|
+
patches?: Array<{ path: string; content: string }>;
|
|
51
|
+
artifacts?: Array<{ path: string; content: string }>;
|
|
52
|
+
streamLogPath?: string | null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type SandboxProvider = {
|
|
56
|
+
id: string;
|
|
57
|
+
run: (request: SandboxProviderRequest) => Promise<SandboxProviderResult> | SandboxProviderResult;
|
|
58
|
+
cleanup?: (request: SandboxProviderRequest) => Promise<void> | void;
|
|
59
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SandboxRuntime } from "./SandboxRuntime.ts";
|
|
2
|
+
import type { SandboxPortMapping, SandboxVolumeMount, SandboxWorkspaceSpec } from "./SandboxHandle.ts";
|
|
2
3
|
|
|
3
4
|
export type SandboxTransportConfig = {
|
|
4
5
|
runId: string;
|
|
@@ -6,4 +7,11 @@ export type SandboxTransportConfig = {
|
|
|
6
7
|
runtime: SandboxRuntime;
|
|
7
8
|
rootDir: string;
|
|
8
9
|
image?: string;
|
|
10
|
+
allowNetwork?: boolean;
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
ports?: SandboxPortMapping[];
|
|
13
|
+
volumes?: SandboxVolumeMount[];
|
|
14
|
+
memoryLimit?: string;
|
|
15
|
+
cpuLimit?: string;
|
|
16
|
+
workspace?: SandboxWorkspaceSpec;
|
|
9
17
|
};
|
package/src/bundle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
1
|
+
import { lstat, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, relative } from "node:path";
|
|
3
3
|
import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
4
4
|
import { assertJsonPayloadWithinBounds, assertOptionalArrayMaxLength, assertOptionalStringMaxLength, } from "@smithers-orchestrator/db/input-bounds";
|
|
@@ -27,12 +27,17 @@ async function walkFiles(dir) {
|
|
|
27
27
|
const entries = await readdir(current, { withFileTypes: true });
|
|
28
28
|
for (const entry of entries) {
|
|
29
29
|
const full = join(current, entry.name);
|
|
30
|
-
|
|
30
|
+
const info = await lstat(full);
|
|
31
|
+
if (info.isSymbolicLink()) {
|
|
32
|
+
throw new SmithersError("TOOL_PATH_ESCAPE", "Sandbox bundle may not contain symlinks.", {
|
|
33
|
+
path: relative(dir, full),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (info.isDirectory()) {
|
|
31
37
|
pending.push(full);
|
|
32
38
|
}
|
|
33
|
-
else if (
|
|
39
|
+
else if (info.isFile()) {
|
|
34
40
|
files.push(full);
|
|
35
|
-
const info = await stat(full);
|
|
36
41
|
totalBytes += info.size;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -70,6 +75,9 @@ function parseReadmeJson(readme) {
|
|
|
70
75
|
patches: Array.isArray(manifest.patches)
|
|
71
76
|
? manifest.patches.filter((v) => typeof v === "string")
|
|
72
77
|
: undefined,
|
|
78
|
+
diffBundle: manifest.diffBundle && typeof manifest.diffBundle === "object"
|
|
79
|
+
? manifest.diffBundle
|
|
80
|
+
: undefined,
|
|
73
81
|
};
|
|
74
82
|
}
|
|
75
83
|
/**
|
|
@@ -85,7 +93,7 @@ function assertPatchPathSafe(bundlePath, patchPath) {
|
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
/**
|
|
88
|
-
* @param {{ output: unknown; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; runId?: string; status: "finished" | "failed" | "cancelled"; streamLogPath?: string | null; }} params
|
|
96
|
+
* @param {{ output: unknown; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; runId?: string; status: "finished" | "failed" | "cancelled"; streamLogPath?: string | null; diffBundle?: unknown; }} params
|
|
89
97
|
*/
|
|
90
98
|
async function estimateBundleWriteBytes(params) {
|
|
91
99
|
const readmeBytes = Buffer.byteLength(JSON.stringify({
|
|
@@ -93,6 +101,7 @@ async function estimateBundleWriteBytes(params) {
|
|
|
93
101
|
status: params.status,
|
|
94
102
|
runId: params.runId,
|
|
95
103
|
patches: (params.patches ?? []).map((patch) => patch.path),
|
|
104
|
+
diffBundle: params.diffBundle,
|
|
96
105
|
}, null, 2), "utf8");
|
|
97
106
|
const patchBytes = (params.patches ?? []).reduce((total, patch) => total + Buffer.byteLength(patch.content, "utf8"), 0);
|
|
98
107
|
const artifactBytes = (params.artifacts ?? []).reduce((total, artifact) => total + Buffer.byteLength(artifact.content, "utf8"), 0);
|
|
@@ -102,7 +111,7 @@ async function estimateBundleWriteBytes(params) {
|
|
|
102
111
|
return readmeBytes + patchBytes + artifactBytes + streamLogBytes;
|
|
103
112
|
}
|
|
104
113
|
/**
|
|
105
|
-
* @param {{ bundlePath: string; output: unknown; status: "finished" | "failed" | "cancelled"; runId?: string; streamLogPath?: string | null; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; }} params
|
|
114
|
+
* @param {{ bundlePath: string; output: unknown; status: "finished" | "failed" | "cancelled"; runId?: string; streamLogPath?: string | null; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; diffBundle?: unknown; }} params
|
|
106
115
|
*/
|
|
107
116
|
async function validateSandboxBundleWriteParams(params) {
|
|
108
117
|
assertOptionalStringMaxLength("bundlePath", params.bundlePath, SANDBOX_BUNDLE_PATH_MAX_LENGTH);
|
|
@@ -133,7 +142,10 @@ async function validateSandboxBundleWriteParams(params) {
|
|
|
133
142
|
*/
|
|
134
143
|
export async function validateSandboxBundle(bundlePath) {
|
|
135
144
|
const resolvedReadme = resolveSandboxPath(bundlePath, "README.md");
|
|
136
|
-
const readmeStats = await
|
|
145
|
+
const readmeStats = await lstat(resolvedReadme).catch(() => null);
|
|
146
|
+
if (readmeStats?.isSymbolicLink()) {
|
|
147
|
+
throw new SmithersError("TOOL_PATH_ESCAPE", "Sandbox bundle README.md may not be a symlink.", { bundlePath });
|
|
148
|
+
}
|
|
137
149
|
if (!readmeStats?.isFile()) {
|
|
138
150
|
throw new SmithersError("INVALID_INPUT", "Sandbox bundle is missing README.md", { bundlePath });
|
|
139
151
|
}
|
|
@@ -160,7 +172,10 @@ export async function validateSandboxBundle(bundlePath) {
|
|
|
160
172
|
assertPatchPathSafe(bundlePath, patchPath);
|
|
161
173
|
}
|
|
162
174
|
const logsPath = resolveSandboxPath(bundlePath, "logs/stream.ndjson");
|
|
163
|
-
const logsStats = await
|
|
175
|
+
const logsStats = await lstat(logsPath).catch(() => null);
|
|
176
|
+
if (logsStats?.isSymbolicLink()) {
|
|
177
|
+
throw new SmithersError("TOOL_PATH_ESCAPE", "Sandbox bundle logs may not be a symlink.", { bundlePath });
|
|
178
|
+
}
|
|
164
179
|
return {
|
|
165
180
|
manifest,
|
|
166
181
|
bundleSizeBytes: walked.totalBytes,
|
|
@@ -170,7 +185,7 @@ export async function validateSandboxBundle(bundlePath) {
|
|
|
170
185
|
};
|
|
171
186
|
}
|
|
172
187
|
/**
|
|
173
|
-
* @param {{ bundlePath: string; output: unknown; status: "finished" | "failed" | "cancelled"; runId?: string; streamLogPath?: string | null; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; }} params
|
|
188
|
+
* @param {{ bundlePath: string; output: unknown; status: "finished" | "failed" | "cancelled"; runId?: string; streamLogPath?: string | null; patches?: Array<{ path: string; content: string }>; artifacts?: Array<{ path: string; content: string }>; diffBundle?: unknown; }} params
|
|
174
189
|
*/
|
|
175
190
|
export async function writeSandboxBundle(params) {
|
|
176
191
|
await validateSandboxBundleWriteParams(params);
|
|
@@ -197,5 +212,6 @@ export async function writeSandboxBundle(params) {
|
|
|
197
212
|
status: params.status,
|
|
198
213
|
runId: params.runId,
|
|
199
214
|
patches: (params.patches ?? []).map((p) => p.path),
|
|
215
|
+
diffBundle: params.diffBundle,
|
|
200
216
|
}, null, 2), "utf8");
|
|
201
217
|
}
|
|
@@ -6,6 +6,7 @@ import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
|
6
6
|
import { spawnCaptureEffect } from "@smithers-orchestrator/driver/child-process";
|
|
7
7
|
import { toSmithersError } from "@smithers-orchestrator/errors/toSmithersError";
|
|
8
8
|
import { SandboxEntityExecutor } from "./sandbox-entity.js";
|
|
9
|
+
import { dockerArgs, normalizeSandboxHandleControls, sandboxRunnerEnv, spawnSandboxCommand } from "./process-runner.js";
|
|
9
10
|
/** @typedef {import("../SandboxTransportConfig.ts").SandboxTransportConfig} SandboxTransportConfig */
|
|
10
11
|
/** @typedef {import("../SandboxHandle.ts").SandboxHandle} SandboxHandle */
|
|
11
12
|
/**
|
|
@@ -14,6 +15,7 @@ import { SandboxEntityExecutor } from "./sandbox-entity.js";
|
|
|
14
15
|
*/
|
|
15
16
|
function baseHandle(config) {
|
|
16
17
|
const sandboxRoot = join(config.rootDir, ".smithers", "sandboxes", config.runId, config.sandboxId);
|
|
18
|
+
const controls = normalizeSandboxHandleControls(config);
|
|
17
19
|
return {
|
|
18
20
|
runtime: config.runtime,
|
|
19
21
|
runId: config.runId,
|
|
@@ -21,6 +23,9 @@ function baseHandle(config) {
|
|
|
21
23
|
sandboxRoot,
|
|
22
24
|
requestPath: join(sandboxRoot, "request"),
|
|
23
25
|
resultPath: join(sandboxRoot, "result"),
|
|
26
|
+
image: config.image,
|
|
27
|
+
allowNetwork: Boolean(config.allowNetwork),
|
|
28
|
+
...controls,
|
|
24
29
|
};
|
|
25
30
|
}
|
|
26
31
|
/** @type {Layer.Layer<SandboxEntityExecutor, never, never>} */
|
|
@@ -29,7 +34,7 @@ export const DockerSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, Sa
|
|
|
29
34
|
const handle = baseHandle(config);
|
|
30
35
|
yield* spawnCaptureEffect("docker", ["info"], {
|
|
31
36
|
cwd: config.rootDir,
|
|
32
|
-
env:
|
|
37
|
+
env: sandboxRunnerEnv(),
|
|
33
38
|
timeoutMs: 10_000,
|
|
34
39
|
maxOutputBytes: 200_000,
|
|
35
40
|
}).pipe(Effect.catchAll(() => Effect.fail(new SmithersError("PROCESS_SPAWN_FAILED", "Docker daemon not reachable.", { runtime: "docker" }))));
|
|
@@ -50,9 +55,15 @@ export const DockerSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, Sa
|
|
|
50
55
|
},
|
|
51
56
|
catch: (cause) => toSmithersError(cause, "ship docker bundle"),
|
|
52
57
|
}),
|
|
53
|
-
execute: (
|
|
58
|
+
execute: (command, handle) => spawnSandboxCommand("docker", dockerArgs(command, handle), {
|
|
59
|
+
cwd: handle.requestPath,
|
|
60
|
+
runtime: "docker",
|
|
61
|
+
}),
|
|
54
62
|
collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
|
|
55
|
-
cleanup: (
|
|
63
|
+
cleanup: (handle) => Effect.tryPromise({
|
|
64
|
+
try: () => rm(handle.requestPath, { recursive: true, force: true }),
|
|
65
|
+
catch: (cause) => toSmithersError(cause, "cleanup docker sandbox workspace"),
|
|
66
|
+
}),
|
|
56
67
|
}));
|
|
57
68
|
/** @type {Layer.Layer<SandboxEntityExecutor, never, never>} */
|
|
58
69
|
export const CodeplaneSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, SandboxEntityExecutor.of({
|
|
@@ -83,8 +94,15 @@ export const CodeplaneSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor,
|
|
|
83
94
|
},
|
|
84
95
|
catch: (cause) => toSmithersError(cause, "ship codeplane bundle"),
|
|
85
96
|
}),
|
|
86
|
-
execute: (
|
|
97
|
+
execute: (command, handle) => Effect.fail(new SmithersError("SANDBOX_EXECUTION_FAILED", "Codeplane sandbox command execution requires the remote Codeplane worker integration.", {
|
|
98
|
+
runtime: "codeplane",
|
|
99
|
+
command,
|
|
100
|
+
workspaceId: handle.workspaceId ?? null,
|
|
101
|
+
})),
|
|
87
102
|
collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
|
|
88
|
-
cleanup: (
|
|
103
|
+
cleanup: (handle) => Effect.tryPromise({
|
|
104
|
+
try: () => rm(handle.requestPath, { recursive: true, force: true }),
|
|
105
|
+
catch: (cause) => toSmithersError(cause, "cleanup codeplane sandbox workspace"),
|
|
106
|
+
}),
|
|
89
107
|
}));
|
|
90
108
|
export const SandboxHttpRunner = HttpRunner;
|