microsandbox-mcp 0.5.7 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -27
- package/dist/config.d.ts +10 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +71 -0
- package/dist/resources.js.map +1 -0
- package/dist/schemas/common.d.ts +7 -0
- package/dist/schemas/common.js +8 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/state/exec-sessions.d.ts +19 -0
- package/dist/state/exec-sessions.js +151 -0
- package/dist/state/exec-sessions.js.map +1 -0
- package/dist/tools/exec.js +283 -56
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/filesystem.js +195 -59
- package/dist/tools/filesystem.js.map +1 -1
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.js +99 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/logs.d.ts +2 -0
- package/dist/tools/logs.js +128 -0
- package/dist/tools/logs.js.map +1 -0
- package/dist/tools/metrics.js +48 -65
- package/dist/tools/metrics.js.map +1 -1
- package/dist/tools/runtime.d.ts +2 -0
- package/dist/tools/runtime.js +61 -0
- package/dist/tools/runtime.js.map +1 -0
- package/dist/tools/sandbox.d.ts +216 -0
- package/dist/tools/sandbox.js +619 -202
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/snapshot.d.ts +2 -0
- package/dist/tools/snapshot.js +234 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/tools/ssh.d.ts +2 -0
- package/dist/tools/ssh.js +323 -0
- package/dist/tools/ssh.js.map +1 -0
- package/dist/tools/volume.js +287 -23
- package/dist/tools/volume.js.map +1 -1
- package/dist/utils/errors.d.ts +2 -7
- package/dist/utils/errors.js +6 -5
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/exec-output.d.ts +20 -0
- package/dist/utils/exec-output.js +15 -0
- package/dist/utils/exec-output.js.map +1 -0
- package/dist/utils/output.d.ts +11 -0
- package/dist/utils/output.js +21 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/policy.d.ts +8 -0
- package/dist/utils/policy.js +47 -0
- package/dist/utils/policy.js.map +1 -0
- package/dist/utils/redaction.d.ts +1 -0
- package/dist/utils/redaction.js +15 -0
- package/dist/utils/redaction.js.map +1 -0
- package/dist/utils/response.d.ts +34 -0
- package/dist/utils/response.js +38 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/utils/serialization.d.ts +15 -0
- package/dist/utils/serialization.js +164 -0
- package/dist/utils/serialization.js.map +1 -0
- package/package.json +5 -3
package/dist/tools/sandbox.js
CHANGED
|
@@ -1,60 +1,175 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { Sandbox } from "microsandbox";
|
|
3
3
|
import { formatError } from "../utils/errors.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
import { formatExecOutput } from "../utils/exec-output.js";
|
|
5
|
+
import { assertHostPathAllowed } from "../utils/policy.js";
|
|
6
|
+
import { ok } from "../utils/response.js";
|
|
7
|
+
import { sandboxHandleData, sandboxSummaryData } from "../utils/serialization.js";
|
|
8
|
+
const statusSchema = z.enum(["running", "stopped", "crashed", "draining", "all"]);
|
|
9
|
+
const selectorSchema = z.object({
|
|
10
|
+
name: z.string().optional().describe("Sandbox name"),
|
|
11
|
+
names: z.array(z.string()).optional().describe("Sandbox names"),
|
|
12
|
+
labels: z.record(z.string(), z.string()).optional().describe("AND-matched labels"),
|
|
13
|
+
status: statusSchema.optional().describe("Status filter"),
|
|
14
|
+
});
|
|
15
|
+
const mountSchema = z.object({
|
|
16
|
+
kind: z.enum(["bind", "named", "tmpfs", "disk"]),
|
|
17
|
+
guestPath: z.string(),
|
|
18
|
+
hostPath: z.string().optional(),
|
|
19
|
+
name: z.string().optional(),
|
|
20
|
+
source: z.string().optional(),
|
|
21
|
+
readonly: z.boolean().optional(),
|
|
22
|
+
noexec: z.boolean().optional(),
|
|
23
|
+
nosuid: z.boolean().optional(),
|
|
24
|
+
nodev: z.boolean().optional(),
|
|
25
|
+
sizeMib: z.number().int().positive().optional(),
|
|
26
|
+
format: z.enum(["raw", "qcow2", "vmdk"]).optional(),
|
|
27
|
+
fstype: z.string().optional(),
|
|
28
|
+
statVirtualization: z.enum(["strict", "relaxed", "off"]).optional(),
|
|
29
|
+
hostPermissions: z.enum(["private", "mirror"]).optional(),
|
|
30
|
+
});
|
|
31
|
+
const patchSchema = z.object({
|
|
32
|
+
kind: z.enum(["text", "file", "copyFile", "copyDir", "mkdir", "append", "remove", "symlink"]),
|
|
33
|
+
type: z.enum(["text", "file", "copyFile", "copyDir", "mkdir", "append", "remove", "symlink"]).optional(),
|
|
34
|
+
path: z.string().optional(),
|
|
35
|
+
guestPath: z.string().optional(),
|
|
36
|
+
hostPath: z.string().optional(),
|
|
37
|
+
content: z.string().optional(),
|
|
38
|
+
contentBase64: z.string().optional(),
|
|
39
|
+
target: z.string().optional(),
|
|
40
|
+
linkPath: z.string().optional(),
|
|
41
|
+
mode: z.number().int().optional(),
|
|
42
|
+
replace: z.boolean().optional(),
|
|
43
|
+
});
|
|
44
|
+
const rootfsSchema = z.discriminatedUnion("kind", [
|
|
45
|
+
z.object({
|
|
46
|
+
kind: z.literal("oci"),
|
|
47
|
+
reference: z.string(),
|
|
48
|
+
pullPolicy: z.enum(["always", "if-missing", "never"]).optional(),
|
|
49
|
+
upperSizeMib: z.number().int().positive().optional(),
|
|
50
|
+
registry: z.object({
|
|
51
|
+
insecure: z.boolean().optional(),
|
|
52
|
+
caCertsPath: z.string().optional(),
|
|
53
|
+
auth: z.object({
|
|
54
|
+
kind: z.enum(["anonymous", "basic"]),
|
|
55
|
+
username: z.string().optional(),
|
|
56
|
+
password: z.string().optional(),
|
|
57
|
+
}).optional(),
|
|
58
|
+
}).optional(),
|
|
59
|
+
}),
|
|
60
|
+
z.object({ kind: z.literal("bind"), path: z.string() }),
|
|
61
|
+
z.object({
|
|
62
|
+
kind: z.literal("disk"),
|
|
63
|
+
path: z.string(),
|
|
64
|
+
fstype: z.string().optional(),
|
|
65
|
+
}),
|
|
66
|
+
z.object({ kind: z.literal("snapshot"), pathOrName: z.string() }),
|
|
67
|
+
]);
|
|
68
|
+
export const sandboxCreateSchema = z.object({
|
|
69
|
+
name: z.string().optional(),
|
|
70
|
+
image: z.string().optional().describe("Convenience OCI image or local rootfs path"),
|
|
71
|
+
rootfs: rootfsSchema.optional(),
|
|
72
|
+
cpus: z.number().int().min(1).max(16).optional(),
|
|
73
|
+
memoryMib: z.number().int().min(128).optional(),
|
|
74
|
+
workdir: z.string().optional(),
|
|
75
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
76
|
+
volumes: z.array(mountSchema).optional(),
|
|
77
|
+
mounts: z.array(mountSchema).optional(),
|
|
78
|
+
patches: z.array(patchSchema).optional(),
|
|
79
|
+
entrypoint: z.array(z.string()).optional(),
|
|
80
|
+
hostname: z.string().optional(),
|
|
81
|
+
maxDuration: z.number().int().positive().optional(),
|
|
82
|
+
idleTimeout: z.number().int().positive().optional(),
|
|
83
|
+
resources: z.object({
|
|
84
|
+
cpus: z.number().int().min(1).max(16).optional(),
|
|
85
|
+
memoryMib: z.number().int().min(128).optional(),
|
|
86
|
+
}).optional(),
|
|
87
|
+
process: z.object({
|
|
88
|
+
workdir: z.string().optional(),
|
|
89
|
+
shell: z.string().optional(),
|
|
90
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
91
|
+
labels: z.record(z.string(), z.string()).optional(),
|
|
92
|
+
entrypoint: z.array(z.string()).optional(),
|
|
93
|
+
user: z.string().optional(),
|
|
94
|
+
scripts: z.record(z.string(), z.string()).optional(),
|
|
95
|
+
rlimits: z.array(z.object({
|
|
96
|
+
resource: z.string(),
|
|
97
|
+
soft: z.number().int().nonnegative(),
|
|
98
|
+
hard: z.number().int().nonnegative().optional(),
|
|
99
|
+
})).optional(),
|
|
100
|
+
init: z.object({
|
|
101
|
+
path: z.string(),
|
|
102
|
+
args: z.array(z.string()).optional(),
|
|
103
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
104
|
+
}).optional(),
|
|
105
|
+
}).optional(),
|
|
106
|
+
lifecycle: z.object({
|
|
107
|
+
detached: z.boolean().optional(),
|
|
108
|
+
replace: z.boolean().optional(),
|
|
109
|
+
replaceTimeoutMs: z.number().int().nonnegative().optional(),
|
|
110
|
+
maxDurationSecs: z.number().int().positive().optional(),
|
|
111
|
+
idleTimeoutSecs: z.number().int().positive().optional(),
|
|
112
|
+
logLevel: z.enum(["error", "warn", "info", "debug", "trace"]).optional(),
|
|
113
|
+
metricsSampleIntervalMs: z.number().int().nonnegative().optional(),
|
|
114
|
+
disableMetricsSample: z.boolean().optional(),
|
|
115
|
+
quietLogs: z.boolean().optional(),
|
|
116
|
+
}).optional(),
|
|
117
|
+
network: z.object({
|
|
118
|
+
disabled: z.boolean().optional(),
|
|
119
|
+
ports: z.array(z.object({
|
|
120
|
+
hostPort: z.number().int().min(1).max(65535),
|
|
121
|
+
guestPort: z.number().int().min(1).max(65535),
|
|
122
|
+
bindAddress: z.string().optional(),
|
|
123
|
+
protocol: z.enum(["tcp", "udp"]).optional(),
|
|
124
|
+
})).optional(),
|
|
125
|
+
maxConnections: z.number().int().positive().optional(),
|
|
126
|
+
trustHostCas: z.boolean().optional(),
|
|
127
|
+
ipv4Pool: z.string().optional(),
|
|
128
|
+
ipv6Pool: z.string().optional(),
|
|
129
|
+
dns: z.object({
|
|
130
|
+
allowRebind: z.boolean().optional(),
|
|
131
|
+
nameservers: z.array(z.string()).optional(),
|
|
132
|
+
queryTimeoutMs: z.number().int().positive().optional(),
|
|
133
|
+
}).optional(),
|
|
134
|
+
tls: z.object({
|
|
135
|
+
ports: z.array(z.number().int().min(1).max(65535)).optional(),
|
|
136
|
+
bypassDomains: z.array(z.string()).optional(),
|
|
137
|
+
blockQuic: z.boolean().optional(),
|
|
138
|
+
caCertPath: z.string().optional(),
|
|
139
|
+
caKeyPath: z.string().optional(),
|
|
140
|
+
upstreamCaCertPaths: z.array(z.string()).optional(),
|
|
141
|
+
trustHostCas: z.boolean().optional(),
|
|
142
|
+
}).optional(),
|
|
143
|
+
}).optional(),
|
|
144
|
+
secrets: z.array(z.object({
|
|
145
|
+
envVar: z.string(),
|
|
146
|
+
value: z.string().optional(),
|
|
147
|
+
valueEnv: z.string().optional(),
|
|
148
|
+
allowedHost: z.string(),
|
|
149
|
+
})).optional(),
|
|
150
|
+
});
|
|
151
|
+
const createSchema = sandboxCreateSchema;
|
|
18
152
|
export function registerSandboxTools(server) {
|
|
19
153
|
server.registerTool("sandbox_run", {
|
|
20
154
|
title: "Run Command in Ephemeral Sandbox",
|
|
21
|
-
description: "Create an ephemeral sandbox, run a shell command, return
|
|
22
|
-
|
|
23
|
-
inputSchema: z.object({
|
|
24
|
-
image: z.string().describe('OCI image (e.g. "python:3.12", "node:22", "alpine")'),
|
|
155
|
+
description: "Create an ephemeral sandbox, run a shell command, return output, and destroy it.",
|
|
156
|
+
inputSchema: createSchema.extend({
|
|
25
157
|
command: z.string().describe("Shell command to execute"),
|
|
26
|
-
memoryMib: z.number().int().min(128).optional().describe("Memory in MiB (default: 512)"),
|
|
27
|
-
cpus: z.number().int().min(1).max(16).optional().describe("Number of vCPUs (default: 1)"),
|
|
28
|
-
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
29
158
|
}),
|
|
30
159
|
annotations: {
|
|
31
160
|
readOnlyHint: false,
|
|
32
161
|
destructiveHint: false,
|
|
33
162
|
idempotentHint: false,
|
|
34
163
|
},
|
|
35
|
-
}, async (
|
|
36
|
-
const name = `mcp-run-${Date.now()}`;
|
|
164
|
+
}, async (args) => {
|
|
165
|
+
const name = args.name ?? `mcp-run-${Date.now()}`;
|
|
37
166
|
let sandbox;
|
|
38
167
|
try {
|
|
39
|
-
|
|
40
|
-
.image(image)
|
|
41
|
-
.memory(memoryMib ?? 512)
|
|
42
|
-
.cpus(cpus ?? 1);
|
|
43
|
-
if (env)
|
|
44
|
-
builder = builder.envs(env);
|
|
168
|
+
const builder = buildSandbox(name, args, false);
|
|
45
169
|
sandbox = await builder.create();
|
|
46
|
-
const output = await sandbox.shell(command);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
type: "text",
|
|
50
|
-
text: JSON.stringify({
|
|
51
|
-
stdout: output.stdout(),
|
|
52
|
-
stderr: output.stderr(),
|
|
53
|
-
exitCode: output.code,
|
|
54
|
-
success: output.success,
|
|
55
|
-
}, null, 2),
|
|
56
|
-
}],
|
|
57
|
-
};
|
|
170
|
+
const output = await sandbox.shell(args.command);
|
|
171
|
+
const result = formatExecOutput(output);
|
|
172
|
+
return ok({ name, ...result.data }, { truncated: result.truncated });
|
|
58
173
|
}
|
|
59
174
|
catch (error) {
|
|
60
175
|
return formatError(error);
|
|
@@ -74,117 +189,51 @@ export function registerSandboxTools(server) {
|
|
|
74
189
|
});
|
|
75
190
|
server.registerTool("sandbox_create", {
|
|
76
191
|
title: "Create Sandbox",
|
|
77
|
-
description: "Create and boot a persistent named sandbox
|
|
78
|
-
|
|
79
|
-
inputSchema: z.object({
|
|
192
|
+
description: "Create and boot a persistent named sandbox.",
|
|
193
|
+
inputSchema: createSchema.extend({
|
|
80
194
|
name: z.string().describe("Unique sandbox name"),
|
|
81
|
-
image: z.string().describe('OCI image reference (e.g. "python:3.12", "ubuntu:24.04")'),
|
|
82
|
-
cpus: z.number().int().min(1).max(16).optional().describe("Virtual CPUs (default: 1)"),
|
|
83
|
-
memoryMib: z.number().int().min(128).optional().describe("Memory in MiB (default: 512)"),
|
|
84
|
-
workdir: z.string().optional().describe("Default working directory inside the sandbox"),
|
|
85
|
-
env: z.record(z.string(), z.string()).optional().describe("Environment variables"),
|
|
86
|
-
volumes: z.array(z.object({
|
|
87
|
-
guestPath: z.string().describe("Mount point inside sandbox"),
|
|
88
|
-
type: z.enum(["bind", "named", "tmpfs"]).describe("Volume type"),
|
|
89
|
-
source: z.string().optional().describe("Host path (bind) or volume name (named)"),
|
|
90
|
-
readonly: z.boolean().optional().describe("Mount as read-only"),
|
|
91
|
-
sizeMib: z.number().optional().describe("Size for tmpfs volumes"),
|
|
92
|
-
})).optional().describe("Volume mounts"),
|
|
93
|
-
patches: z.array(z.object({
|
|
94
|
-
type: z.enum(["text", "mkdir", "append", "remove", "symlink"]).describe("Patch type"),
|
|
95
|
-
path: z.string().describe("Target path inside sandbox"),
|
|
96
|
-
content: z.string().optional().describe("File content (text, append)"),
|
|
97
|
-
target: z.string().optional().describe("Symlink target"),
|
|
98
|
-
mode: z.number().optional().describe("Unix permissions"),
|
|
99
|
-
})).optional().describe("Rootfs modifications applied before boot"),
|
|
100
|
-
entrypoint: z.array(z.string()).optional().describe("Override image entrypoint"),
|
|
101
|
-
hostname: z.string().optional().describe("Guest hostname"),
|
|
102
|
-
maxDuration: z.number().optional().describe("Auto-stop after N seconds"),
|
|
103
|
-
idleTimeout: z.number().optional().describe("Auto-stop after N seconds of inactivity"),
|
|
104
195
|
}),
|
|
105
196
|
annotations: {
|
|
106
197
|
readOnlyHint: false,
|
|
107
198
|
destructiveHint: false,
|
|
108
199
|
idempotentHint: false,
|
|
109
200
|
},
|
|
110
|
-
}, async (
|
|
201
|
+
}, async (args) => {
|
|
111
202
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
if (workdir)
|
|
117
|
-
builder = builder.workdir(workdir);
|
|
118
|
-
if (env)
|
|
119
|
-
builder = builder.envs(env);
|
|
120
|
-
if (entrypoint)
|
|
121
|
-
builder = builder.entrypoint(entrypoint);
|
|
122
|
-
if (hostname)
|
|
123
|
-
builder = builder.hostname(hostname);
|
|
124
|
-
if (maxDuration)
|
|
125
|
-
builder = builder.maxDuration(maxDuration);
|
|
126
|
-
if (idleTimeout)
|
|
127
|
-
builder = builder.idleTimeout(idleTimeout);
|
|
128
|
-
if (volumes) {
|
|
129
|
-
for (const v of volumes) {
|
|
130
|
-
builder = builder.volume(v.guestPath, (m) => {
|
|
131
|
-
switch (v.type) {
|
|
132
|
-
case "bind": {
|
|
133
|
-
let b = m.bind(v.source);
|
|
134
|
-
if (v.readonly)
|
|
135
|
-
b = b.readonly();
|
|
136
|
-
return b;
|
|
137
|
-
}
|
|
138
|
-
case "named": {
|
|
139
|
-
let b = m.named(v.source);
|
|
140
|
-
if (v.readonly)
|
|
141
|
-
b = b.readonly();
|
|
142
|
-
return b;
|
|
143
|
-
}
|
|
144
|
-
case "tmpfs": {
|
|
145
|
-
let b = m.tmpfs();
|
|
146
|
-
if (typeof v.sizeMib === "number")
|
|
147
|
-
b = b.size(v.sizeMib);
|
|
148
|
-
if (v.readonly)
|
|
149
|
-
b = b.readonly();
|
|
150
|
-
return b;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
203
|
+
const builder = buildSandbox(args.name, args, true);
|
|
204
|
+
const sandbox = await builder.create();
|
|
205
|
+
if (args.lifecycle?.detached !== false) {
|
|
206
|
+
await sandbox.detach();
|
|
155
207
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
}
|
|
208
|
+
return ok({ name: args.name, status: "running" });
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return formatError(error);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
server.registerTool("sandbox_start", {
|
|
215
|
+
title: "Start Sandboxes",
|
|
216
|
+
description: "Start stopped sandboxes by name or selector.",
|
|
217
|
+
inputSchema: selectorSchema,
|
|
218
|
+
annotations: {
|
|
219
|
+
readOnlyHint: false,
|
|
220
|
+
destructiveHint: false,
|
|
221
|
+
idempotentHint: true,
|
|
222
|
+
},
|
|
223
|
+
}, async (selector) => {
|
|
224
|
+
try {
|
|
225
|
+
const handles = await resolveSandboxHandles(selector, { requireSelector: true });
|
|
226
|
+
const results = [];
|
|
227
|
+
for (const handle of handles) {
|
|
228
|
+
if (handle.status === "running") {
|
|
229
|
+
results.push({ name: handle.name, started: false, alreadyRunning: true });
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const sandbox = await handle.startDetached();
|
|
233
|
+
await sandbox.detach();
|
|
234
|
+
results.push({ name: handle.name, started: true });
|
|
180
235
|
}
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
content: [{
|
|
184
|
-
type: "text",
|
|
185
|
-
text: JSON.stringify({ name, status: "running", image }, null, 2),
|
|
186
|
-
}],
|
|
187
|
-
};
|
|
236
|
+
return ok(results);
|
|
188
237
|
}
|
|
189
238
|
catch (error) {
|
|
190
239
|
return formatError(error);
|
|
@@ -192,24 +241,36 @@ export function registerSandboxTools(server) {
|
|
|
192
241
|
});
|
|
193
242
|
server.registerTool("sandbox_list", {
|
|
194
243
|
title: "List Sandboxes",
|
|
195
|
-
description: "List
|
|
196
|
-
inputSchema:
|
|
244
|
+
description: "List sandboxes with status and summary metadata.",
|
|
245
|
+
inputSchema: selectorSchema,
|
|
246
|
+
annotations: {
|
|
247
|
+
readOnlyHint: true,
|
|
248
|
+
destructiveHint: false,
|
|
249
|
+
idempotentHint: true,
|
|
250
|
+
},
|
|
251
|
+
}, async (selector) => {
|
|
252
|
+
try {
|
|
253
|
+
const handles = await resolveSandboxHandles(selector, { defaultAll: true });
|
|
254
|
+
return ok(handles.map(sandboxSummaryData));
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return formatError(error);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
server.registerTool("sandbox_status", {
|
|
261
|
+
title: "Sandbox Status",
|
|
262
|
+
description: "Show status for one sandbox or a filtered sandbox set.",
|
|
263
|
+
inputSchema: selectorSchema,
|
|
197
264
|
annotations: {
|
|
198
265
|
readOnlyHint: true,
|
|
199
266
|
destructiveHint: false,
|
|
200
267
|
idempotentHint: true,
|
|
201
268
|
},
|
|
202
|
-
}, async () => {
|
|
269
|
+
}, async (selector) => {
|
|
203
270
|
try {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
status: h.status,
|
|
208
|
-
createdAt: h.createdAt,
|
|
209
|
-
}));
|
|
210
|
-
return {
|
|
211
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
212
|
-
};
|
|
271
|
+
const status = selector.status ?? (selector.name || selector.names ? "all" : "running");
|
|
272
|
+
const handles = await resolveSandboxHandles({ ...selector, status }, { defaultAll: true });
|
|
273
|
+
return ok(handles.map(sandboxSummaryData));
|
|
213
274
|
}
|
|
214
275
|
catch (error) {
|
|
215
276
|
return formatError(error);
|
|
@@ -229,87 +290,443 @@ export function registerSandboxTools(server) {
|
|
|
229
290
|
}, async ({ name }) => {
|
|
230
291
|
try {
|
|
231
292
|
const handle = await Sandbox.get(name);
|
|
232
|
-
return
|
|
233
|
-
content: [{
|
|
234
|
-
type: "text",
|
|
235
|
-
text: JSON.stringify({
|
|
236
|
-
name: handle.name,
|
|
237
|
-
status: handle.status,
|
|
238
|
-
config: handle.configJson,
|
|
239
|
-
createdAt: handle.createdAt,
|
|
240
|
-
updatedAt: handle.updatedAt,
|
|
241
|
-
}, null, 2),
|
|
242
|
-
}],
|
|
243
|
-
};
|
|
293
|
+
return ok(sandboxHandleData(handle));
|
|
244
294
|
}
|
|
245
295
|
catch (error) {
|
|
246
296
|
return formatError(error);
|
|
247
297
|
}
|
|
248
298
|
});
|
|
249
299
|
server.registerTool("sandbox_stop", {
|
|
250
|
-
title: "Stop
|
|
251
|
-
description: "Stop
|
|
252
|
-
inputSchema:
|
|
253
|
-
|
|
254
|
-
|
|
300
|
+
title: "Stop Sandboxes",
|
|
301
|
+
description: "Stop running sandboxes gracefully or forcefully.",
|
|
302
|
+
inputSchema: selectorSchema.extend({
|
|
303
|
+
force: z.boolean().optional(),
|
|
304
|
+
timeoutMs: z.number().int().nonnegative().optional(),
|
|
255
305
|
}),
|
|
256
306
|
annotations: {
|
|
257
307
|
readOnlyHint: false,
|
|
258
308
|
destructiveHint: true,
|
|
259
|
-
idempotentHint:
|
|
309
|
+
idempotentHint: true,
|
|
260
310
|
},
|
|
261
|
-
}, async ({
|
|
311
|
+
}, async ({ force, timeoutMs, ...selector }) => {
|
|
262
312
|
try {
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
313
|
+
const handles = await resolveSandboxHandles(selector, { requireSelector: true });
|
|
314
|
+
const results = [];
|
|
315
|
+
for (const handle of handles) {
|
|
316
|
+
if (handle.status !== "running") {
|
|
317
|
+
results.push({ name: handle.name, stopped: false, alreadyStopped: true, status: handle.status });
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (force) {
|
|
321
|
+
if (typeof timeoutMs === "number")
|
|
322
|
+
await handle.killWithTimeout(timeoutMs);
|
|
323
|
+
else
|
|
324
|
+
await handle.kill();
|
|
325
|
+
}
|
|
326
|
+
else if (typeof timeoutMs === "number") {
|
|
327
|
+
await handle.stopWithTimeout(timeoutMs);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
await handle.stop();
|
|
331
|
+
}
|
|
332
|
+
results.push({ name: handle.name, stopped: true });
|
|
266
333
|
}
|
|
267
|
-
|
|
268
|
-
|
|
334
|
+
return ok(results);
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
return formatError(error);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
server.registerTool("sandbox_drain", {
|
|
341
|
+
title: "Drain Sandboxes",
|
|
342
|
+
description: "Request graceful drain for running sandboxes.",
|
|
343
|
+
inputSchema: selectorSchema,
|
|
344
|
+
annotations: {
|
|
345
|
+
readOnlyHint: false,
|
|
346
|
+
destructiveHint: false,
|
|
347
|
+
idempotentHint: true,
|
|
348
|
+
},
|
|
349
|
+
}, async (selector) => {
|
|
350
|
+
try {
|
|
351
|
+
const handles = await resolveSandboxHandles(selector, { requireSelector: true });
|
|
352
|
+
const results = [];
|
|
353
|
+
for (const handle of handles) {
|
|
354
|
+
if (handle.status !== "running") {
|
|
355
|
+
results.push({ name: handle.name, drainRequested: false, status: handle.status });
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
await handle.requestDrain();
|
|
359
|
+
results.push({ name: handle.name, drainRequested: true });
|
|
269
360
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
361
|
+
return ok(results);
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
return formatError(error);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
server.registerTool("sandbox_wait", {
|
|
368
|
+
title: "Wait For Sandboxes",
|
|
369
|
+
description: "Wait until selected sandboxes reach a terminal state.",
|
|
370
|
+
inputSchema: selectorSchema,
|
|
371
|
+
annotations: {
|
|
372
|
+
readOnlyHint: true,
|
|
373
|
+
destructiveHint: false,
|
|
374
|
+
idempotentHint: true,
|
|
375
|
+
},
|
|
376
|
+
}, async (selector) => {
|
|
377
|
+
try {
|
|
378
|
+
const handles = await resolveSandboxHandles(selector, { requireSelector: true });
|
|
379
|
+
const results = [];
|
|
380
|
+
for (const handle of handles) {
|
|
381
|
+
results.push(await handle.waitUntilStopped());
|
|
382
|
+
}
|
|
383
|
+
return ok(results);
|
|
274
384
|
}
|
|
275
385
|
catch (error) {
|
|
276
386
|
return formatError(error);
|
|
277
387
|
}
|
|
278
388
|
});
|
|
279
389
|
server.registerTool("sandbox_remove", {
|
|
280
|
-
title: "Remove
|
|
281
|
-
description: "Remove
|
|
282
|
-
inputSchema:
|
|
283
|
-
|
|
284
|
-
force: z.boolean().optional().describe("Stop and remove in one step if still running"),
|
|
390
|
+
title: "Remove Sandboxes",
|
|
391
|
+
description: "Remove stopped sandboxes. Use force to stop running sandboxes first.",
|
|
392
|
+
inputSchema: selectorSchema.extend({
|
|
393
|
+
force: z.boolean().optional(),
|
|
285
394
|
}),
|
|
286
395
|
annotations: {
|
|
287
396
|
readOnlyHint: false,
|
|
288
397
|
destructiveHint: true,
|
|
289
398
|
idempotentHint: true,
|
|
290
399
|
},
|
|
291
|
-
}, async ({
|
|
400
|
+
}, async ({ force, ...selector }) => {
|
|
292
401
|
try {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
await waitForStopped(name);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
catch {
|
|
302
|
-
// Sandbox may already be stopped or not exist.
|
|
402
|
+
const handles = await resolveSandboxHandles(selector, { requireSelector: true });
|
|
403
|
+
const results = [];
|
|
404
|
+
for (const handle of handles) {
|
|
405
|
+
if (force && handle.status === "running") {
|
|
406
|
+
await handle.kill();
|
|
303
407
|
}
|
|
408
|
+
await Sandbox.remove(handle.name);
|
|
409
|
+
results.push({ name: handle.name, removed: true });
|
|
304
410
|
}
|
|
305
|
-
|
|
306
|
-
return {
|
|
307
|
-
content: [{ type: "text", text: JSON.stringify({ name, removed: true }, null, 2) }],
|
|
308
|
-
};
|
|
411
|
+
return ok(results);
|
|
309
412
|
}
|
|
310
413
|
catch (error) {
|
|
311
414
|
return formatError(error);
|
|
312
415
|
}
|
|
313
416
|
});
|
|
314
417
|
}
|
|
418
|
+
async function waitForStopped(name, timeoutMs = 10000) {
|
|
419
|
+
const start = Date.now();
|
|
420
|
+
while (Date.now() - start < timeoutMs) {
|
|
421
|
+
try {
|
|
422
|
+
const h = await Sandbox.get(name);
|
|
423
|
+
if (h.status !== "running")
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function buildSandbox(name, args, persistent) {
|
|
433
|
+
let builder = Sandbox.builder(name);
|
|
434
|
+
builder = applyRootfs(builder, args);
|
|
435
|
+
const cpus = args.resources?.cpus ?? args.cpus;
|
|
436
|
+
const memoryMib = args.resources?.memoryMib ?? args.memoryMib;
|
|
437
|
+
const workdir = args.process?.workdir ?? args.workdir;
|
|
438
|
+
const env = { ...(args.env ?? {}), ...(args.process?.env ?? {}) };
|
|
439
|
+
const entrypoint = args.process?.entrypoint ?? args.entrypoint;
|
|
440
|
+
const maxDuration = args.lifecycle?.maxDurationSecs ?? args.maxDuration;
|
|
441
|
+
const idleTimeout = args.lifecycle?.idleTimeoutSecs ?? args.idleTimeout;
|
|
442
|
+
if (cpus)
|
|
443
|
+
builder = builder.cpus(cpus);
|
|
444
|
+
if (memoryMib)
|
|
445
|
+
builder = builder.memory(memoryMib);
|
|
446
|
+
if (workdir)
|
|
447
|
+
builder = builder.workdir(workdir);
|
|
448
|
+
if (args.process?.shell)
|
|
449
|
+
builder = builder.shell(args.process.shell);
|
|
450
|
+
if (Object.keys(env).length > 0)
|
|
451
|
+
builder = builder.envs(env);
|
|
452
|
+
if (args.process?.labels)
|
|
453
|
+
builder = builder.labels(args.process.labels);
|
|
454
|
+
if (entrypoint)
|
|
455
|
+
builder = builder.entrypoint(entrypoint);
|
|
456
|
+
if (args.hostname)
|
|
457
|
+
builder = builder.hostname(args.hostname);
|
|
458
|
+
if (args.process?.user)
|
|
459
|
+
builder = builder.user(args.process.user);
|
|
460
|
+
if (args.process?.scripts)
|
|
461
|
+
builder = builder.scripts(args.process.scripts);
|
|
462
|
+
if (maxDuration)
|
|
463
|
+
builder = builder.maxDuration(maxDuration);
|
|
464
|
+
if (idleTimeout)
|
|
465
|
+
builder = builder.idleTimeout(idleTimeout);
|
|
466
|
+
if (args.lifecycle?.replaceTimeoutMs !== undefined)
|
|
467
|
+
builder = builder.replaceWithTimeout(args.lifecycle.replaceTimeoutMs);
|
|
468
|
+
else if (args.lifecycle?.replace)
|
|
469
|
+
builder = builder.replace();
|
|
470
|
+
if (args.lifecycle?.logLevel)
|
|
471
|
+
builder = builder.logLevel(args.lifecycle.logLevel);
|
|
472
|
+
if (args.lifecycle?.metricsSampleIntervalMs !== undefined)
|
|
473
|
+
builder = builder.metricsSampleIntervalMs(args.lifecycle.metricsSampleIntervalMs);
|
|
474
|
+
if (args.lifecycle?.disableMetricsSample)
|
|
475
|
+
builder = builder.disableMetricsSample();
|
|
476
|
+
if (args.lifecycle?.quietLogs)
|
|
477
|
+
builder = builder.quietLogs();
|
|
478
|
+
builder = builder.detached(args.lifecycle?.detached ?? persistent);
|
|
479
|
+
if (args.process?.init) {
|
|
480
|
+
const init = args.process.init;
|
|
481
|
+
if (init.env && Object.keys(init.env).length > 0) {
|
|
482
|
+
builder = builder.initWith(init.path, (b) => {
|
|
483
|
+
let acc = init.args ? b.args(init.args) : b;
|
|
484
|
+
acc = acc.envs(init.env);
|
|
485
|
+
return acc;
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
builder = builder.init(init.path, init.args);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
for (const rlimit of args.process?.rlimits ?? []) {
|
|
493
|
+
builder = builder.rlimitRange(rlimit.resource, rlimit.soft, rlimit.hard ?? rlimit.soft);
|
|
494
|
+
}
|
|
495
|
+
for (const mount of [...(args.volumes ?? []), ...(args.mounts ?? [])]) {
|
|
496
|
+
builder = applyMount(builder, mount);
|
|
497
|
+
}
|
|
498
|
+
if (args.patches && args.patches.length > 0) {
|
|
499
|
+
builder = builder.patch((p) => {
|
|
500
|
+
let acc = p;
|
|
501
|
+
for (const patch of args.patches ?? []) {
|
|
502
|
+
const kind = patch.kind ?? patch.type;
|
|
503
|
+
switch (kind) {
|
|
504
|
+
case "text":
|
|
505
|
+
acc = acc.text(requiredPath(patch), patch.content ?? "", { mode: patch.mode, replace: patch.replace });
|
|
506
|
+
break;
|
|
507
|
+
case "file":
|
|
508
|
+
acc = acc.file(requiredPath(patch), Buffer.from(patch.contentBase64 ?? "", "base64"), { mode: patch.mode, replace: patch.replace });
|
|
509
|
+
break;
|
|
510
|
+
case "copyFile":
|
|
511
|
+
acc = acc.copyFile(assertHostPathAllowed(requiredHostPath(patch)), requiredGuestPath(patch), { mode: patch.mode, replace: patch.replace });
|
|
512
|
+
break;
|
|
513
|
+
case "copyDir":
|
|
514
|
+
acc = acc.copyDir(assertHostPathAllowed(requiredHostPath(patch)), requiredGuestPath(patch), { replace: patch.replace });
|
|
515
|
+
break;
|
|
516
|
+
case "mkdir":
|
|
517
|
+
acc = acc.mkdir(requiredPath(patch), { mode: patch.mode });
|
|
518
|
+
break;
|
|
519
|
+
case "append":
|
|
520
|
+
acc = acc.append(requiredPath(patch), patch.content ?? "");
|
|
521
|
+
break;
|
|
522
|
+
case "remove":
|
|
523
|
+
acc = acc.remove(requiredPath(patch));
|
|
524
|
+
break;
|
|
525
|
+
case "symlink":
|
|
526
|
+
acc = acc.symlink(patch.target ?? "", patch.linkPath ?? requiredPath(patch), { replace: patch.replace });
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return acc;
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
if (args.network || args.secrets) {
|
|
534
|
+
builder = builder.network((n) => {
|
|
535
|
+
let acc = n;
|
|
536
|
+
if (args.network?.disabled)
|
|
537
|
+
acc = acc.enabled(false);
|
|
538
|
+
for (const port of args.network?.ports ?? []) {
|
|
539
|
+
const protocol = port.protocol ?? "tcp";
|
|
540
|
+
if (protocol === "udp" && port.bindAddress)
|
|
541
|
+
acc = acc.portUdpBind(port.bindAddress, port.hostPort, port.guestPort);
|
|
542
|
+
else if (protocol === "udp")
|
|
543
|
+
acc = acc.portUdp(port.hostPort, port.guestPort);
|
|
544
|
+
else if (port.bindAddress)
|
|
545
|
+
acc = acc.portBind(port.bindAddress, port.hostPort, port.guestPort);
|
|
546
|
+
else
|
|
547
|
+
acc = acc.port(port.hostPort, port.guestPort);
|
|
548
|
+
}
|
|
549
|
+
if (args.network?.maxConnections)
|
|
550
|
+
acc = acc.maxConnections(args.network.maxConnections);
|
|
551
|
+
if (args.network?.trustHostCas || args.network?.tls?.trustHostCas)
|
|
552
|
+
acc = acc.trustHostCAs(true);
|
|
553
|
+
if (args.network?.ipv4Pool)
|
|
554
|
+
acc = acc.ipv4Pool(args.network.ipv4Pool);
|
|
555
|
+
if (args.network?.ipv6Pool)
|
|
556
|
+
acc = acc.ipv6Pool(args.network.ipv6Pool);
|
|
557
|
+
if (args.network?.dns) {
|
|
558
|
+
const dns = args.network.dns;
|
|
559
|
+
acc = acc.dns((b) => {
|
|
560
|
+
let next = b;
|
|
561
|
+
if (dns.allowRebind !== undefined)
|
|
562
|
+
next = next.rebindProtection(!dns.allowRebind);
|
|
563
|
+
if (dns.nameservers)
|
|
564
|
+
next = next.nameservers(dns.nameservers);
|
|
565
|
+
if (dns.queryTimeoutMs)
|
|
566
|
+
next = next.queryTimeoutMs(dns.queryTimeoutMs);
|
|
567
|
+
return next;
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
if (args.network?.tls) {
|
|
571
|
+
const tls = args.network.tls;
|
|
572
|
+
acc = acc.tls((b) => {
|
|
573
|
+
let next = b;
|
|
574
|
+
for (const bypass of tls.bypassDomains ?? [])
|
|
575
|
+
next = next.bypass(bypass);
|
|
576
|
+
if (tls.ports)
|
|
577
|
+
next = next.interceptedPorts(tls.ports);
|
|
578
|
+
if (tls.blockQuic !== undefined)
|
|
579
|
+
next = next.blockQuic(tls.blockQuic);
|
|
580
|
+
if (tls.caCertPath)
|
|
581
|
+
next = next.interceptCaCert(assertHostPathAllowed(tls.caCertPath));
|
|
582
|
+
if (tls.caKeyPath)
|
|
583
|
+
next = next.interceptCaKey(assertHostPathAllowed(tls.caKeyPath));
|
|
584
|
+
for (const ca of tls.upstreamCaCertPaths ?? [])
|
|
585
|
+
next = next.upstreamCaCert(assertHostPathAllowed(ca));
|
|
586
|
+
return next;
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
for (const secret of args.secrets ?? []) {
|
|
590
|
+
const value = secret.value ?? (secret.valueEnv ? process.env[secret.valueEnv] : undefined);
|
|
591
|
+
if (value === undefined)
|
|
592
|
+
throw new Error(`missing secret value for ${secret.envVar}`);
|
|
593
|
+
acc = acc.secretEnvSimple(secret.envVar, value, secret.allowedHost);
|
|
594
|
+
}
|
|
595
|
+
return acc;
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return builder;
|
|
599
|
+
}
|
|
600
|
+
function applyRootfs(builder, args) {
|
|
601
|
+
const rootfs = args.rootfs;
|
|
602
|
+
if (rootfs) {
|
|
603
|
+
switch (rootfs.kind) {
|
|
604
|
+
case "oci":
|
|
605
|
+
builder = builder.imageWith((image) => {
|
|
606
|
+
let acc = image.oci(rootfs.reference);
|
|
607
|
+
if (rootfs.upperSizeMib)
|
|
608
|
+
acc = acc.upperSize(rootfs.upperSizeMib);
|
|
609
|
+
return acc;
|
|
610
|
+
});
|
|
611
|
+
if (rootfs.pullPolicy)
|
|
612
|
+
builder = builder.pullPolicy(rootfs.pullPolicy);
|
|
613
|
+
if (rootfs.registry) {
|
|
614
|
+
const registry = rootfs.registry;
|
|
615
|
+
builder = builder.registry((r) => {
|
|
616
|
+
let acc = r;
|
|
617
|
+
if (registry.insecure)
|
|
618
|
+
acc = acc.insecure();
|
|
619
|
+
if (registry.caCertsPath)
|
|
620
|
+
acc = acc.caCertsPath(assertHostPathAllowed(registry.caCertsPath));
|
|
621
|
+
if (registry.auth)
|
|
622
|
+
acc = acc.auth(registry.auth);
|
|
623
|
+
return acc;
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
return builder;
|
|
627
|
+
case "bind":
|
|
628
|
+
return builder.image(assertHostPathAllowed(rootfs.path));
|
|
629
|
+
case "disk":
|
|
630
|
+
return builder.imageWith((image) => {
|
|
631
|
+
let acc = image.disk(assertHostPathAllowed(rootfs.path));
|
|
632
|
+
if (rootfs.fstype)
|
|
633
|
+
acc = acc.fstype(rootfs.fstype);
|
|
634
|
+
return acc;
|
|
635
|
+
});
|
|
636
|
+
case "snapshot":
|
|
637
|
+
return builder.fromSnapshot(resolvePathOrName(rootfs.pathOrName));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (!args.image)
|
|
641
|
+
throw new Error("sandbox_create requires rootfs or image");
|
|
642
|
+
return builder.image(resolvePathOrName(args.image));
|
|
643
|
+
}
|
|
644
|
+
function applyMount(builder, mount) {
|
|
645
|
+
return builder.volume(mount.guestPath, (m) => {
|
|
646
|
+
let acc = m;
|
|
647
|
+
switch (mount.kind) {
|
|
648
|
+
case "bind":
|
|
649
|
+
acc = acc.bind(assertHostPathAllowed(mount.hostPath ?? mount.source ?? ""));
|
|
650
|
+
break;
|
|
651
|
+
case "named":
|
|
652
|
+
acc = acc.named(mount.name ?? mount.source ?? "");
|
|
653
|
+
break;
|
|
654
|
+
case "tmpfs":
|
|
655
|
+
acc = acc.tmpfs();
|
|
656
|
+
if (mount.sizeMib)
|
|
657
|
+
acc = acc.size(mount.sizeMib);
|
|
658
|
+
break;
|
|
659
|
+
case "disk":
|
|
660
|
+
acc = acc.disk(assertHostPathAllowed(mount.hostPath ?? mount.source ?? ""));
|
|
661
|
+
if (mount.format)
|
|
662
|
+
acc = acc.format(mount.format);
|
|
663
|
+
if (mount.fstype)
|
|
664
|
+
acc = acc.fstype(mount.fstype);
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
if (mount.readonly)
|
|
668
|
+
acc = acc.readonly();
|
|
669
|
+
if (mount.noexec)
|
|
670
|
+
acc = acc.noexec();
|
|
671
|
+
if (mount.nosuid)
|
|
672
|
+
acc = acc.nosuid();
|
|
673
|
+
if (mount.nodev)
|
|
674
|
+
acc = acc.nodev();
|
|
675
|
+
if (mount.statVirtualization)
|
|
676
|
+
acc = acc.statVirtualization(mount.statVirtualization);
|
|
677
|
+
if (mount.hostPermissions)
|
|
678
|
+
acc = acc.hostPermissions(mount.hostPermissions);
|
|
679
|
+
return acc;
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
async function resolveSandboxHandles(selector, options = {}) {
|
|
683
|
+
const names = [...(selector.name ? [selector.name] : []), ...(selector.names ?? [])];
|
|
684
|
+
const hasSelector = names.length > 0 || Object.keys(selector.labels ?? {}).length > 0 || selector.status !== undefined;
|
|
685
|
+
if (options.requireSelector && !hasSelector)
|
|
686
|
+
throw new Error("at least one selector is required");
|
|
687
|
+
const map = new Map();
|
|
688
|
+
for (const name of names) {
|
|
689
|
+
const handle = await Sandbox.get(name);
|
|
690
|
+
map.set(handle.name, handle);
|
|
691
|
+
}
|
|
692
|
+
if (Object.keys(selector.labels ?? {}).length > 0) {
|
|
693
|
+
for (const handle of await Sandbox.listWith({ labels: selector.labels })) {
|
|
694
|
+
map.set(handle.name, handle);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else if (names.length === 0 && (selector.status !== undefined || options.defaultAll)) {
|
|
698
|
+
for (const handle of await Sandbox.list()) {
|
|
699
|
+
map.set(handle.name, handle);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
let handles = [...map.values()];
|
|
703
|
+
if (selector.status && selector.status !== "all") {
|
|
704
|
+
handles = handles.filter((handle) => handle.status === selector.status);
|
|
705
|
+
}
|
|
706
|
+
handles.sort((left, right) => left.name.localeCompare(right.name));
|
|
707
|
+
return handles;
|
|
708
|
+
}
|
|
709
|
+
function resolvePathOrName(value) {
|
|
710
|
+
return looksLikePath(value) ? assertHostPathAllowed(value) : value;
|
|
711
|
+
}
|
|
712
|
+
function looksLikePath(value) {
|
|
713
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("~");
|
|
714
|
+
}
|
|
715
|
+
function requiredPath(patch) {
|
|
716
|
+
const path = patch.path ?? patch.guestPath;
|
|
717
|
+
if (!path)
|
|
718
|
+
throw new Error(`${patch.kind} patch requires path`);
|
|
719
|
+
return path;
|
|
720
|
+
}
|
|
721
|
+
function requiredGuestPath(patch) {
|
|
722
|
+
const path = patch.guestPath ?? patch.path;
|
|
723
|
+
if (!path)
|
|
724
|
+
throw new Error(`${patch.kind} patch requires guestPath`);
|
|
725
|
+
return path;
|
|
726
|
+
}
|
|
727
|
+
function requiredHostPath(patch) {
|
|
728
|
+
if (!patch.hostPath)
|
|
729
|
+
throw new Error(`${patch.kind} patch requires hostPath`);
|
|
730
|
+
return patch.hostPath;
|
|
731
|
+
}
|
|
315
732
|
//# sourceMappingURL=sandbox.js.map
|